如何在MVC 3 Razor中将动态数据写入页面布局?

16
我有一个使用Razor引擎的MVC 3 C#项目。有哪些方法和最佳实践可以将动态数据写入_Layout.cshtml中?
例如,我想在网站的右上角显示用户的姓名,并根据登录的用户从Session、数据库或其他地方获取该名称。
更新:我还在寻找一种在布局的head元素中呈现特定数据的良好实践。例如,如果我需要根据已登录用户的凭据渲染特定的CSS文件。
(对于上面的例子,我考虑使用Url Helpers。)
2个回答

20

Visual Studio创建的默认网络应用程序使用_LogOnPartial.cshtml来执行这个操作。

User Name值在HomeController的LogOn动作中设置。

_LogOnPartial.cshtml的代码

@if(Request.IsAuthenticated) {
    <text>Welcome <strong>@User.Identity.Name</strong>!
    [ @Html.ActionLink("Log Off", "LogOff", "Account") ]</text>
}
else {
    @:[ @Html.ActionLink("Log On", "LogOn", "Account") ]
}

User.Identity是AspNet Membership提供程序的一部分。

_Layout.cshtml的代码。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
</head>
<body>
    <div class="page">
        <header>
            <div id="title">
                <h1>Test</h1>
            </div>
            <div id="logindisplay">
                @Html.Partial("_LogOnPartial")
            </div>
            <nav>
                <ul id="menu">
                </ul>
            </nav>
        </header>
        <section id="main">
            @RenderBody()
        </section>
        <footer>
        </footer>
    </div>
</body>
</html>

账户控制器登录操作的代码

[HttpPost]
        public ActionResult LogOn(LogOnModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                if (Membership.ValidateUser(model.UserName, model.Password))
                {
                    FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
                    if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                        && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
                    {
                        return Redirect(returnUrl);
                    }
                    else
                    {
                        return RedirectToAction("Index", "Home");
                    }
                }
                else
                {
                    ModelState.AddModelError("", "The user name or password provided is incorrect.");
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

ApplicationViewPage类的代码

public abstract class ApplicationViewPage<T> : WebViewPage<T>
    {
        protected override void InitializePage()
        {
            SetViewBagDefaultProperties();
            base.InitializePage();
        }

        private void SetViewBagDefaultProperties()
        {
            ViewBag.LayoutModel = new LayoutModel(Request.ServerVariables["SERVER_NAME"]);
        }

    }

上述代码允许我在每个页面上拥有一个 ViewBag.LayoutModel,其中包含我的 LayoutModel 类的一个实例。

这是我的 LayoutModel 类的代码

public class LayoutModel
    {
        public string LayoutFile { get; set; }
        public string IpsTop { get; set; }
        public string IpsBottom { get; set; }
        public string ProfileTop { get; set; }
        public string ProfileBottom { get; set; }

        public LayoutModel(string hostname)
        {
            switch (hostname.ToLower())
            {
                default:

                    LayoutFile = "~/Views/Shared/_BnlLayout.cshtml";
                    IpsBottom = "~/Template/_BnlIpsBottom.cshtml";
                    IpsTop = "~/Template/_BnlTop.cshtml";
                    ProfileTop = "~/Template/_BnlProfileTop.cshtml";
                    break;

                case "something.com":
                    LayoutFile = "~/Views/Shared/_Layout.cshtml";
                    IpsBottom = "~/Template/_somethingBottom.cshtml";
                    IpsTop = "~/Template/_somethingTop.cshtml";
                    ProfileTop = "~/Template/_somethingProfileTop.cshtml";
                    break;
            }
        }
    }

这里是View的代码

@{
    ViewBag.Title = "PageTitle";
    Layout = @ViewBag.LayoutModel.LayoutFile; 
}
@using (Html.BeginForm())
{
    <span class="error">@ViewBag.ErrorMessage</span>
    <input type="hidden" name="Referrer" id="Referrer" value="@ViewBag.Referrer" />
    html stuff here       
}

请参考下面的问题了解更多细节。确保按照那里描述的修改您的web.config:如何设置ViewBag属性,而不使用控制器的基类为所有视图?


@atbebtg - 所以基本上只需使用部分视图,并在布局中使用@Html.Partial()进行呈现? - Dimskiy
没错,只需使用部分视图并执行@Html.Partial()即可。 - atbebtg
如果是要放入<head>元素的数据呢?比如CSS文件的路径或其他什么东西? - Dimskiy
我相信你也可以做同样的事情,但我从未尝试过。我使用@RenderSection("Script", false)来创建一个用于放置所有JavaScript代码的部分,该部分位于页面底部。通过声明一个名为"Script"的部分,并将其所需属性设置为false,我可以在需要时向该部分添加更多代码。 - atbebtg
是的,那些是特定于页面的脚本,根据正在呈现的视图是否链接到或不链接。如果我想根据已登录的用户链接到特定的CSS文件怎么办?我不想在每个视图中都有那个逻辑。此外,我习惯将视图视为<body>元素的一部分,而不是<head>。 - Dimskiy
这可能不是你的问题的答案,但可能与之相关。我有一个类似的情况,我想根据网站的URL显示不同的布局文件。我本来打算为每个域名使用不同的CSS文件,但后来我决定使用不同的布局文件更加灵活。我所做的是创建了一个继承自WebViewPage<T>的新类。由于代码太长无法在这里放置,我将用以下代码修改我的答案。 - atbebtg

1
除了atbebtg的答案之外,要将内容呈现到头部,您需要利用Razor的section支持。Sections是模板化HTML的命名片段,可以在视图中定义并在布局中呈现,布局会根据需要看到这些片段。在布局中,您调用@RenderSection("wellKnownSectionName"),在使用布局的视图中,您声明@section wellKnownSectionName { <link rel="stylesheet" href="@UserStylesheetUrl" /><script type="text/javascript" src="@UserScriptUrl"> }。通常,您希望在名称中描述section的意图,例如“documentHead”。
更新: 如果您在每个视图上呈现相同的模板化HTML,则应将其放入布局中。(由于您的布局包括HEAD和BODY标记,因此您只需将适当的代码添加到其HEAD标记即可。)您只需要确保通过ViewBag/View.Model/ViewData从控制器传递必要的布局信息。因此,您的布局将包括以下内容:
<head>
    <link rel="stylesheet" href="/css/@ViewBag.UserName/.css"/>
</head>

你的控制器将包含填充 ViewBag.UserName 的逻辑:

ViewBag.UserName = Session["UserName"];

(理想情况下,您应该使用强类型视图模型,并建议您避免使用Session来存储任何内容,因为它的好处与替代方案相比很小,并且对架构的成本很大...相反,我建议您在浏览器上存储一些加密的cookie,其中包含用户名或其他信息,您可以在每个页面加载时使用它从缓存/数据库/服务中检索用户对象。)

谢谢!但是如果对于所有视图来说数据都是相同的呢?我不想在每个具有相同渲染逻辑的视图中都使用 @section wellKnownSection.....,我更愿意将这个逻辑放在一个地方。 - Dimskiy
好的,我同意关于会话的观点,但并不完全同意关于cookie的观点。但是你所说的控制器是什么?<head>元素在布局中定义。布局没有控制器。一些基本/共享控制器吗?(附言:实际上,我考虑使用自定义URL助手来处理我的CSS URL示例。目前想不到其他要放入<head>的数据 :).....) - Dimskiy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接