“母版页”应该放在哪里 MVC 中的逻辑?

发布于 2024-07-14 14:41:12 字数 631 浏览 11 评论 0原文

我正在尝试 MVC,我的问题是 - 我在带有 WebForms 的母版页中的 Page_Load 逻辑在哪里,它应该在 MVC 中的哪里?这是业务案例:

  • 不同的主机标头应该导致不同的页面标题显示在网站的(一个)母版页上,即所有页面上。 例如,如果主机标头为 hello.mydomain.com,则所有页面/视图的页面标题应为“Hello World”,而所有页面/视图的 goodbye.mydomain.com 应为“Goodbye World”。
  • 如果主机头与列表中的任何内容不同,无论应用程序中的哪个位置,它都应该重定向到/Error/NoHostHeader。

以前,我将其放在 MasterPage Load() 事件中,看起来像在 MVC 中,我可以在每个控制器中执行此操作(感觉必须在每个控制器中调用此功能并不正确),或者在某个地方执行此操作Global.asax(似乎太……全球性了?)。

编辑:我已经使用 Global.asax 方法与控制器相结合来成功地工作来实际处理数据。 此时唯一的问题是,所有主机头信息都在数据库中。 如果您愿意,我通常会将“租户”信息存储在会话变量中,并且仅在该信息不存在时才进行数据库调用; 有一个更好的方法吗?

I'm experimenting with MVC, and my question is - where I had Page_Load logic in Master Pages with WebForms, where should it go in MVC? Here's the business case:

  • Different Host Headers should cause different Page Titles to be displayed on the site's (one) Master Page, therefore all pages. For example, if the host header is hello.mydomain.com, the page title should be "Hello World" for all pages/views, while goodbye.mydomain.com should be "Goodbye World" for all pages/views.
  • If the host header is different than anything I have in the list, regardless of where in the application, it should redirect to /Error/NoHostHeader.

Previously, I'd put this in the MasterPage Load() event, and it looks like in MVC, I could do this either in every controller (doesn't feel right to have to call this functionality in every controller), or somewhere in Global.asax (seems too... global?).

Edit: I have gotten this to work successfully using the Global.asax method combined with a Controller for actually processing the data. Only problem at this point is, all of the host header information is in a database. I would normally store the "tenant" information if you will in a Session variable and only do a DB call when it's not there; is there a better way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

你是年少的欢喜 2024-07-21 14:41:12

由于某种原因,MVC 中没有 1:1 等效项,让我们概括一下如何以 MVC 方式思考它:

模型:“此站点的页面总是在特定上下文中被请求,我们称之为租户(或用户、主题或您的子域代表的任何内容)。域模型具有代表当前请求的租户的属性。”

视图:“根据模型中设置的租户呈现页面标题。”

控制器:“根据主机标头设置模型中的租户”。

请记住,我们要避免的是混合控制器、视图和业务逻辑。 将控制器逻辑放在多个地方或一个地方(不称为“控制器”)不是问题,只要它保持分离即可。

现在好消息是:即使使用 Web 窗体,您也可以实现这种“MVC 风格”,并且该解决方案仍然适用于 ASP.NET MVC!

您仍然拥有请求生命周期(而不是页面生命周期),因此您可以实现一个自定义 HttpModule,其中包含所有请求的这部分控制器逻辑。 它处理 BeginRequest 事件,检查主机标头,并将租户存储到类似 HttpContext.Current.Items["tenant"] 的内容。 (当然,您可以为此字典条目提供一个静态类型化包装器。)

然后您的所有模型对象(或模型基类,或适合您的解决方案的任何对象)都可以访问 HttpContext 以提供对此信息的访问,如下所示:

public string Tenant
{
    get { return HttpContext.Current.Items["tenant"]; }
}

优点:

  • 您已经分离了原因(主机头)和结果(渲染页面标题),提高了可维护性和可测试性
  • 因此您可以根据此状态轻松地向域模型添加其他行为,例如根据当前租户从数据库加载内容。
  • 您可以轻松地使视图的更多部分依赖于租户,例如您包含的 CSS 文件、徽标图像等。
  • 稍后您可以更改控制器逻辑以不仅基于子域,还可以基于在 cookie、引荐来源网址、搜索词、用户代理语言或您能想到的任何内容上,无需根据模型修改任何代码。

更新您的编辑:我不喜欢在会话中保留状态的想法,特别是如果您的会话 cookie 可能不仅适用于每个子域,而且适用于所有域。 在这种情况下,如果用户之前访问过另一个子域,您可能会提供不一致的内容。 数据库中将主机标头映射到租户的信息可能不会经常更改,因此您可以缓存它,并且不需要为每个请求进行数据库查找。

There is no 1:1 equivalent in MVC for a reason, let's just recapitulate how to think about it the MVC way:

Model: "Pages of this site are always requested in a certain context, let's call it the tenant (or user, topic or whatever your sub domains represent). The domain model has a property representing the tenant of the current request."

View: "Render the page title depending on the tenant set in the model."

Controller: "Set the tenant in the model depending on the host header".

Keep in mind that what we want to avoid is mixing controller, view and business logic. Having controller logic in more then one place or a place, that is not called "controller" is not a problem, as long as it remains separated.

And now the good thing: You could do this "MVC style" even with Web Forms, and the solution still works with ASP.NET MVC!

You still have the request lifecycle (not the page lifecycle), so you could implement a custom HttpModule that contains this part of the controller logic for all requests. It handles the BeginRequest event, checks for the host header, and stores the tenant to something like HttpContext.Current.Items["tenant"]. (Of course you could have a static, typed wrapper for this dictionary entry.)

Then all your model objects (or a model base class, or whatever is appropriate for your solution) can access the HttpContext to provide access to this information like this:

public string Tenant
{
    get { return HttpContext.Current.Items["tenant"]; }
}

Advantages:

  • You have separated cause (host header) and effect (rendering page title), improving maintainability and testability
  • Therefore you could easily add additional behavior to your domain model based on this state, like loading content from the database depending on the current tenant.
  • You could easily make more parts of the view depend on the tenant, like CSS file you include, a logo image etc.
  • You could later change the controller logic to set the tenant in the model not only based on the sub domain, but maybe based on a cookie, a referrer, search term, user agent's language, or whatever you can think about, without modifying any of your code depending on the model.

Update re your edit: I don't like the idea of holding the state in the session, especially if your session cookie might apply not only to each sub domain, but to all domains. In this case you might serve inconsistent content if users visted another sub domain before. Probably the information in the database that is mapping host headers to tenants won't change very often, so you can cache it and don't need a database lookup for every request.

痞味浪人 2024-07-21 14:41:12

您可以创建一个基本控制器,为 MVC 母版页视图提供正确的 ViewData,然后从该控制器派生每个实际控制器。 如果将逻辑放入 ActionExecuting 方法中,则应该能够生成异常或在必要时重定向到错误页面。

You could create a base controller that supplied the correct ViewData to your MVC Master Page View, then derive each of your actual controllers from that one. If you put the logic into the ActionExecuting method, you should be able to generate an exception or redirect to an error page if necessary.

半葬歌 2024-07-21 14:41:12

您考虑的太多是“WebForms”,而没有足够的 MVC。 母版页只是视图的包装,它应该只包含布局 html。 你可以向你的主人发送东西,但这是一条单向路,你应该努力寻求不可知论的观点。 底线:忘记 WebForms 拥有的事件,因为它们不会在这里使用。

既然你正在处理主机标头,我想你可以把它放在 Global.asax 中...现在我很困惑:P

来自 http://forums.asp.net/t/1226272.aspx

protected void Application_BeginRequest(object sender, EventArgs e)
        {
            string host = string.Empty;

            if (this.Request.ServerVariables["HTTP_HOST"] == this.Request.Url.DnsSafeHost)
            {
                host = this.Request.Url.DnsSafeHost;
            }
            else
            {
                Regex regex = new Regex("http://[^/]*.host/([^/]*)(/.*)");
                Match match = regex.Match(this.Request.Url.AbsoluteUri);

                if (match.Success)
                {
                    host = match.Groups[1].Value;
                    Context.RewritePath(match.Groups[2].Value);
                }
            }

            // Match the host with the portal in the database
            ...
        } 

You are thinking too "WebForms" and not enough MVC. A master page is just a wrapper of your view, and it should only contain layout html. You can send stuff to your master, but it's a one way road and you should strive for agnostic views. Bottom line: forget about the events that WebForms had as they aren't going to be used here.

Since you are dealing with Host headers I suppose you could put it in the Global.asax...great now I'm confused :P

Stolen code from http://forums.asp.net/t/1226272.aspx

protected void Application_BeginRequest(object sender, EventArgs e)
        {
            string host = string.Empty;

            if (this.Request.ServerVariables["HTTP_HOST"] == this.Request.Url.DnsSafeHost)
            {
                host = this.Request.Url.DnsSafeHost;
            }
            else
            {
                Regex regex = new Regex("http://[^/]*.host/([^/]*)(/.*)");
                Match match = regex.Match(this.Request.Url.AbsoluteUri);

                if (match.Success)
                {
                    host = match.Groups[1].Value;
                    Context.RewritePath(match.Groups[2].Value);
                }
            }

            // Match the host with the portal in the database
            ...
        } 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文