【问题标题】:Abstraction layer between Controller and ViewController和View之间的抽象层
【发布时间】:2014-06-27 16:09:00
【问题描述】:

我正在尝试在我的控制器和我的视图之间创建另一个层,以便我可以根据他们所属的公司的“客户 ID”将不同版本的视图传递给用户。

我有以下代码:

public class HomeController : Controller
{
    //
    // GET: /Home/
    public ActionResult Index()
    {
        // set client
        var client = new Client();
        client.Id = Guid.NewGuid();
        client.Name = "Foo";

        // set user
        var user = new User();
        user.Id = Guid.NewGuid();
        user.ClientId = client.Id;
        user.Name = "Foo";

        return ViewRenderer.RenderView("AddComplete", client);
    }
}

我的 ViewRenderer 类如下所示:

public static class ViewRenderer
{
    public static ViewResult RenderView(string view, Guid clientId)
    {
        string viewName = GetViewForClient(view, clientId);
        return Controller.View(view);
    }

    public static string GetViewForClient(string view, Guid clientId)
    {
        // todo: logic to return view specific to the company to which a user belongs...
    }
}

问题是,RenderView(string view, Guid clientId) 中的 return Controller.View(view); 行给了我错误:

System.Web.Mvc.Controller.View()' 是不可访问的,因为它 防护等级

我很想知道如何解决这个错误,或者是否有更好的方法来做我想做的事情,即显示特定于用户所在公司的不同版本的视图属于。

编辑:我脑子里想的另一个选择...

有没有办法覆盖View() 方法,这样我就可以在它前面加上一个目录名称,例如,属于“Acme Co.”的用户。会像 View("MyView") 那样调用与其他所有人相同的控制器操作,但该方法实际上会调用 View("AcmeCo/MyView") 但是,我实际上并没有在我的控制器中编写该代码,它只是从用户的客户端 ID 属性派生而来。

【问题讨论】:

  • Controller.View() 方法受到保护。除非您从控制器派生您的视图渲染,否则您无法访问受保护的方法。
  • @MichaelG 谢谢,但即使我这样做 public static class ViewRenderer : Controller 它仍然给出同样的错误。
  • 使用派生类时,去掉“Controller.”,使用base.View(view);

标签: c# asp.net-mvc design-patterns asp.net-mvc-5 architecture


【解决方案1】:

您可以只替换视图引擎而不是添加另一个抽象。

编写您自己的视图引擎(这是从 RazorViewEngine 开始的方法)

public class ByIdRazorViewEngine : RazorViewEngine
{
   protected override IView CreateView(ControllerContext controllerContext, 
        string viewPath, string masterPath)
{
    var id = // get something from controller context controllerContext
    var newViewPath = CalculateViewPathFromId(id);

    return base.CreateView(controllerContext, newViewPath, masterPath);
}

并在Global.asax.cs注册:

protected void Application_Start()
{
    ViewEngines.Engines.Clear();

    ViewEngines.Engines.Add(new ByIdRazorViewEngine());
}

【讨论】:

  • 我真的很喜欢这个主意。不过,我不知道这是多少工作量。此外,不确定访问HttpContext 是否容易,以便我可以检索当前用户的 ID 并进行数据库调用以获取他们的客户端 ID 属性。对此有什么想法吗?
  • ControllerContext 上有 RequestContext,如果您涉及数据库访问,您可能希望使用异步过滤器获取客户端 ID
  • 感谢您的想法。但是,当我在 Google async filter mvc 上搜索时,它说 MVC 没有这样的过滤器(尽管 Web API 有)。你能告诉我这个想法的任何链接吗?
  • 该死,你是对的。没有这样的事情......(我在想MVC vNext)。我建议你进行同步,除非你有超大规模的网站,否则你不会注意到差异。或者,您可以将其捕获为身份验证/授权的一部分,然后在视图引擎中使用它
【解决方案2】:

View() 方法是受保护的成员。您只能从派生类型中访问它,例如您的 HomeController 类。另外,您正尝试将其作为静态方法访问。

您可以创建一个基础控制器来公开您的专用视图逻辑。为了便于说明,我将其命名为 DynamicViewControllerBase

public class HomeController : DynamicViewControllerBase
{
    //
    // GET: /Home/
    public ActionResult Index()
    {
        // set client
        var client = new Client();
        client.Id = Guid.NewGuid();
        client.Name = "Foo";

        // set user
        var user = new User();
        user.Id = Guid.NewGuid();
        user.ClientId = client.Id;
        user.Name = "Foo";

        return RenderView("AddComplete", client);
    }
}

public class DynamicViewControllerBase : Controller
{
    protected ViewResult RenderView(string view, Guid clientId)
    {
        string viewName = GetViewForClient(view, clientId);
        return View(view);
    }

    // Unless you plan to use methods and properties within 
    // the instance of `Controller`, you can leave this as 
    // a static method.
    private static string GetViewForClient(string view, Guid clientId)
    {
        // todo: logic to return view...
    }
}

【讨论】:

  • 谢谢,马里奥,我正在考虑类似的事情。
  • +0: @user1477388 请注意,正如 Mario J Vargas 在答案的前半部分注意到的那样,扩展示例中的代码 将无法编译,因为 View 是受保护的方法。考虑只映射视图名称(如return View(ViewMapper.Map("myView", clientId)).
【解决方案3】:

如果您只想拥有控制器前缀的公司名称,请将RoutePrefix 属性应用于您的控制器。

例子:

[RoutePrefix(@"{company}")]
public partial class HomeController : Controller 
{


}

在您的 RouteConfig 文件中,

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    // Make sure this line is added
    routes.MapMvcAttributeRoutes();

}

由于您的用户必须经过身份验证才能登录他们的帐户,一旦他们自己验证了自己的身份,您可以:

  • 在您的用户计算机上存储一个带有公司名称的 cookie
  • 在每次请求时调用您的数据库以检索此信息
  • 利用 ViewData[]
  • 等等..

获得他们公司的名称后,您可以使用该名称构建网址。

例子:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model)
{
    // ... authenticate user etc

    // Redirect to 
    // foo.com/abc/home
    return this.RedirectToAction("Index", "Home", new { company = "abc" });
}

如果您尝试解决此问题,我怀疑您是否能够做到,因为网络请求首先通过路由,并且路由决定执行哪个控制器/操作,但要知道公司名称您的操作需要执行才能检索。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多