【问题标题】:ASP.NET MVC2 call a service method from the master page?ASP.NET MVC2 从母版页调用服务方法?
【发布时间】:2011-05-15 01:34:15
【问题描述】:

在使用 Service - Repository 模式的 MVC2 应用程序中,如何从母版页调用服务方法?

+--------------------------------------+
| Logo                      Welcome xyz|
+--------------------------------------+
| Home | Sales | Import | Admin (menu) |
+--------------------------------------+

在我的菜单中,我现在有一些受用户角色限制访问的页面。我有一个现有的服务方法可以检查当前用户是否可以查看某个页面:

IPageAccessService.CanAccess(int pageId, int roleId);

在控制器方法上,我可以调用它来检查用户是否可以看到页面:

public ActionResult Update(int id?)
{
    if (!_pageAccessService.CanAccess(pageId, roleId))
    {
        return RedirectToAction("Index", "Home");
    }
}

但我不知道如何从我的 Site.Master 调用此方法,这样当它创建菜单时,如果用户无权访问,它就不会显示菜单项(菜单是一个简单的无序列表):

<li><a href="<%=Url.Content("~/Admin") %>">Admin</a>
<ul>
    <li><a href="<%=Url.Content("~/Admin/Roles") %>">User Roles</a></li>
    <li><a href="<%=Url.Content("~/Admin/AdminReports") %>">Admin Reports</a></li>
</ul>
</li>

我猜它需要看起来像这样(在添加到列表之前必须检查每个页面):

if (_pageAccessService.CanAccess(pageId, roleId)) <li><a href="<%=Url.Content("~/Admin") %>">Admin</a>
<ul>
        if (_pageAccessService.CanAccess(pageId, roleId)) <li><a href="<%=Url.Content("~/Admin/Roles") %>">User Roles</a></li>
        if (_pageAccessService.CanAccess(pageId, roleId)) <li><a href="<%=Url.Content("~/Admin/AdminReports") %>">Admin Reports</a></li>
</ul>
</li>

但在我能做到这一点之前,我需要知道如何真正从主服务器调用服务方法。

编辑:

我已经改编了达林的答案,并得到了这个:

public static class LinkExtensions
{
    private static readonly IPageAccessRepository _repo = new PageAccessRepository();
    private static readonly IPageAccessService _pageAccess = new PageAccessService(_repo);

    public static MvcHtmlString MenuItem(
    this HtmlHelper htmlHelper, string linkText,
    string url, string pageName
    )
    {
        if (!_pageAccess.CanAccess(pageName))
        {
            return MvcHtmlString.Empty;
        }
        // The user can access the page => show the menu
        var a = new TagBuilder("a");
        a.Attributes["href"] = url;
        a.SetInnerText(linkText);
        return MvcHtmlString.Create(string.Format("<li>{0}</li>",a));
    }

问题是我仍然需要调用服务,所以我需要能够实例化它。因为它是一个静态类,所以我的 IoC 容器在这里无济于事。所以我仍然必须手动创建服务和存储库。而且它仍然遇到与我原来的丑陋解决方法相同的问题 - 在视图中手动创建存储库。

【问题讨论】:

    标签: c# web-services asp.net-mvc-2 master-pages


    【解决方案1】:

    您可以编写一个自定义 HTML 帮助程序来呈现此菜单的不同项目。在基于用户角色的助手内部,您将决定是否生成给定项目。例如字里行间:

    public static class LinkExtensions
    {
        public static MvcHtmlString MenuItem(
            this HtmlHelper htmlHelper,
            string linkText,
            string url,
            string requiredRole
        )
        {
            var a = new TagBuilder("a");
            a.Attributes["href"] = url;
            a.SetInnerText(linkText);
            if (string.IsNullOrEmpty(requiredRole))
            {
                // No role required => show the menu item
                return MvcHtmlString.Create(a.ToString());
            }
    
            var user = htmlHelper.ViewContext.HttpContext.User;
            if (!user.IsInRole(requiredRole))
            {
                // A role is required but no user authenticated or user is not in role
                // => show empty
                return MvcHtmlString.Empty;
            }
    
            // The user is in role => show the menu
            return MvcHtmlString.Create(a.ToString());
        }
    }
    

    在视图内部:

    <li>
        <%= Html.MenuItem("Admin", Url.Content("~/Admin"), "admin") %>
        <ul>
            <li>
                <%= Html.MenuItem("User Roles", Url.Content("~/Admin/Roles"), "userroles") %>
            </li>
            <li>
                <%= Html.MenuItem("Admin Reports", Url.Content("~/Admin/AdminReports"), "admin") %>
            </li>
        </ul>
    </li>
    

    另一种可能性是使用子动作和主动作中的Html.Action helper

    【讨论】:

    • 这不会导致一堆空的&lt;li&gt; 标签吗?不确定这会对菜单产生什么影响。我刚刚尝试手动添加一个空的&lt;li&gt;,它并没有破坏菜单,所以很好。
    • @JK,是的。您可以在返回之前将它们包含在帮助程序中:return MvcHtmlString.Create("&lt;li&gt;" + a.ToString() + "&lt;/li&gt;");.
    • 谢谢,非常接近。但是我仍然需要调用 IPageAccessService.CanAccess() 方法。因为扩展是静态的,所以我不能使用 IoC 并且仍然必须手动创建一个存储库(问题已更新以显示这一点)
    【解决方案2】:

    任何需要逻辑的东西都不应该出现在视图中,当然也包括它们的助手。

    我建议您为此添加一个新的控制器动作,将您的逻辑添加到动作中,然后直接从控制器返回用户名,或者如果您想显示更多内容,例如登录控件,则可以使用视图。

    因此,例如使用 UsernameIsLoggedIn 属性创建 LoginWelcomeMessage 类。在操作中根据您想要的任何检查设置那些,并将它们发送到基于它们显示/隐藏的视图。

    在您的母版页中,您执行@Html.RenderAction() 以执行带来用户名等的操作。

    可以对整个菜单执行相同的想法。是否将整个标题放在一个操作/视图中或有两个不同的标题取决于您。如果您有相同的视图,则模型可以具有额外的属性,如 CanViewSalesMenuItemsCanViewAdminMenuItems 等,这些属性是从您的操作方法中设置的,并在其视图中用于显示/隐藏项目。

    美妙之处在于母版页不会关心,它只是将整个事情委托给控制器操作及其使用RenderAction()的视图

    【讨论】:

    • 是的,我同意在视图中实例化服务/存储库是不正确的(并且扩展适用于视图的所有意图部分)。我已经设法将菜单切换到使用 Html.Action(其 MVC2)从 site.master 调用的控制器操作。但我并没有完全按照你所说的关于视图中用于隐藏/显示项目的额外属性。
    • 我说的是您添加的操作的视图。通常你会创建一个特殊的类(通常称为 ViewModel,不管类的实际命名如何)。此类应具有特定于 UI 的功能(例如“显示”/“隐藏”/...)。控制器调用业务服务并基于此分配 ViewModel 中的属性,然后将其发送到 View,View 使用它们来呈现自身。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多