从部分视图调用动态菜单

时间:2015-12-28 13:45:50

标签: asp.net-mvc

我的网络应用程序的菜单系统是以Paul Sheriff的课程为蓝本的,它运行良好。我希望通过为每个订户级别以及管理员提供特定菜单来增强其可用性。我的意图是有一个部分视图,它将注入菜单html,该html是从MenuController中的代码派生的,该MenuController从服务库调用我的MenuService中的方法。

这是解决这个问题的正确方法吗?我是MVC的新手,曾经使用过webforms。如果我从布局页面中删除菜单html,那么如何调用带有新菜单代码的partialview?一旦登录用户登录并且应用程序知道需要什么菜单,是否有办法缓存该布局,因此不必为每个页面请求创建菜单?

2 个答案:

答案 0 :(得分:2)

如果我对你的案例的假设是正确的,那么你就是在正确的轨道上。 MVC的目的是像这样使用。

补充Chris Pratt的回答:

[ChildActionOnly]
public ActionResult GetMenu()
{
    // fetch user, subscriber levels, etc.
    return PartialView("_Menu", model);
}

由于对客户端请求的响应,您可能希望使用[ChildActionOnly]来阻止服务器处理此操作方法。使用此属性,部分视图将在使用另一个视图请求时呈现。

鉴于您有部分视图SubMenu.cshtml,在您的MainMenu视图或Layout.cshtml中,您将拥有@Html.RenderPartial("Path_To_SubMenu_View")

关于缓存,以下是此blog的摘录:

在ASP.NET MVC中,缓存完整呈现页面的更简单方法是使用OutputCacheAttribute,如下所示:

[Authorize]
public class DashboardController : Controller 
{
    [OutputCache(Duration = 3600)]
    public ActionResult Index() 
    {
       // Your awesome code goes here
    }
}

之前的代码会使Index操作在缓存中保留一小时。这适用于公共内容,但对于特定于用户的内容,您必须补充缓存键,以便不同的用户不会看到彼此的缓存。

为此,我们使用OutputCacheAttribute的VaryByCustom属性,如下所示:

[Authorize]
public class DashboardController : Controller 
{
    [OutputCache(Duration = 3600, VaryByCustom = "User")]
    public ActionResult Index() 
    {
       // Your awesome code goes here
    }
}

您还需要在通过HttpApplication调用的方法中处理“User”值:GetVaryByCustomString方法。 因此,您需要转到Global.asax.cs并覆盖该方法:

public override string GetVaryByCustomString(HttpContext context, string arg) 
{ 
    if (arg.Equals("User", StringComparison.InvariantCultureIgnoreCase)) 
    {
        var user = context.User.Identity.Name; // TODO here you have to pick an unique identifier from the current user identity
        return string.Format("{0}@{1}", userIdentifier.Name, userIdentifier.Container); 
    }

    return base.GetVaryByCustomString(context, arg); 
}

或者,如果您未在管道中设置自定义主体,则可以查找会话ID,如下所示:

private static SessionStateSection SessionStateSection = (System.Web.Configuration.SessionStateSection)ConfigurationManager.GetSection("system.web/sessionState");

public override string GetVaryByCustomString(HttpContext context, string arg) 
{ 
    if (arg.Equals("User", StringComparison.InvariantCultureIgnoreCase)) 
    {
        var cookieName =  SessionStateSection.CookieName;
        var cookie = context.Request.Cookies[cookieName];
        return cookie.Value;
    }

    return base.GetVaryByCustomString(context, arg); 
}

我正在使用会话状态配置部分来获取会话cookie名称,因此如果您更改了默认的“ASP.NET_SessionId”,它仍然可以工作。

答案 1 :(得分:2)

您需要利用儿童行动:

[Authorize]
public class FooController : Controller
{
    ...

    [AllowAnonymous]
    [ChildActionOnly]
    public ActionResult GetMenu()
    {
        // fetch user, subscriber levels, etc.
        return PartialView("_Menu", model);
    }
}

然后,创建视图Views\Foo\_Menu.cshtml并利用模型中的数据来确定要显示的菜单。最后,在您的布局中,将以下内容添加到您希望显示菜单的位置:

@Html.Action("GetMenu", "Foo")

要缓存结果,您可以使用[OutputCache]修饰子操作,但您需要通过登录用户对其进行更改,因此它将为每个用户进行唯一缓存,而不是在多个用户之间共享。但是,没有内置因用户输出缓存而异,因此您必须实现自己的内置。

在Global.asax中,添加以下内容:

public override string GetVaryByCustomString(HttpContext context, string arg)
{
    if (arg == "user")
    {
        return context.User.Identity.Name;
    }
    return string.Empty;
}

然后,添加输出缓存,如:

[OutputCache(Duration = 3600, VaryByCustom = "user")]
public ActionResult GetMenu()

注意:

  1. FooController可以是您喜欢的任何控制器。
  2. 如果控制器未使用[Authorize]进行修饰,则需要执行子操作。否则,您将无权访问用户主体。但是,在布局中使用时,子操作必须允许匿名访问。否则,您将在登录和注册等任何匿名页面上获得无限重定向。