【问题标题】:Creating ViewResults outside of Controllers in ASP.NET MVC在 ASP.NET MVC 中的控制器之外创建 ViewResults
【发布时间】:2023-03-31 10:10:01
【问题描述】:

我的几个控制器操作都有一套标准的故障处理行为。一般来说,我想:

  • 根据路由数据(ID 等)加载对象
    • 如果路由数据未指向有效对象(例如:通过 URL 黑客攻击),则通知用户该问题并返回 HTTP 404 Not Found
  • 验证当前用户对对象具有适当的权限
    • 如果用户没有权限,通知用户问题并返回 HTTP 403 Forbidden
  • 如果上述操作成功,则对该对象执行特定于操作的操作(即:在视图中呈现它)。

这些步骤非常标准化,我希望有可重用的代码来实现该行为。

我目前的攻击计划是有一个辅助方法来做这样的事情:

public static ActionResult HandleMyObject(this Controller controller, 
    Func<MyObject,ActionResult> onSuccess) {
  var myObject = MyObject.LoadFrom(controller.RouteData).
  if ( myObject == null ) return NotFound(controller);
  if ( myObject.IsNotAllowed(controller.User)) return NotAllowed(controller);
  return onSuccess(myObject);
}

# NotAllowed() is pretty much the same as this
public static NotFound(Controller controller){
    controller.HttpContext.Response.StatusCode = 404
    # NotFound.aspx is a shared view.
    ViewResult result = controller.View("NotFound");
    return result;
}

这里的问题是 Controller.View() 是一个受保护的方法,因此不能从帮助程序访问。我已经研究过显式地创建一个新的 ViewResult 实例,但是有足够多的属性可以设置,我很担心在不知道陷阱的情况下这样做。

从特定控制器外部创建 ViewResult 的最佳方法是什么?

【问题讨论】:

    标签: asp.net-mvc controller


    【解决方案1】:

    请阅读这篇文章,因为我在操作过滤器中遇到了同样的问题。我的解决方案是显式创建视图操作。这是基于 MVC 源的受保护 View() 方法,因此它应该填充所需的属性。无论如何,似乎没有问题。

    public static NotFound(Controller controller){
        controller.HttpContext.Response.StatusCode = 404;
    
        ViewResult result = new ViewResult {
                ViewName = "NotFound",
                ViewData = controller.ViewData,
                TempData = controller.TempData
            };
        return result;
    }
    

    今天有点晚了,但这对我有用。

    【讨论】:

      【解决方案2】:

      在写这篇文章时,我想到了一种方法。

      我可以把上面的代码放在一个帮助器中,而不是把它放到控制器的一个子类中,然后为我的实际控制器子类化这个类。这将允许我调用受保护的 View() 方法。

      我不是特别喜欢这个,因为它requires inheritance to work,但它仍然是一个选项。

      【讨论】:

      • 我不喜欢这个的一点是,它意味着控制器只能有一组共享动作行为:它(单独)继承的行为。对于这个特定的示例,这不是问题,但随着项目的发展,我可以看到它变得更加笨拙。
      • 我知道你的问题已经有好几年了,但我只是想我会提到另一种可能性。你可以在你的基本控制器上公开一个名为GetView()View() 的公共版本,然后你可以将你的助手放在一个单独的类中。你仍然需要将你的控制器传递给你的助手,这有点尴尬,但它确实允许组合而不是继承。
      【解决方案3】:

      我有同样的问题,但回答不同。我真的不想为此使用继承,所以我使用了 lambda。

      首先,我有一个对象从我的控制器传递给我想要返回视图的方法:

      public struct MyControllerContext
      {
          public HttpRequestBase Request { get; set; }
          public HttpResponseBase Response { get; set; }
          public DocsController Controller { get; set; }
      
          public Func<string, object, ViewResult> ViewResult;
          public ViewResult View(string viewName, object model)
          {
              return this.ViewResult(viewName, model);
          }
      }
      

      我创建了一个实例并将它作为参数传递给将返回结果的方法:

      // In the controller
      var context = new DocsControllerContext()
      {
          Request = Request,
          Response = Response,
          Controller = this,
          ViewResult = (viewName, model) =>
          {
              return View(viewName, model);
          }
      };
      
      var returnValue = methodInfo.Invoke(toInvoke, new object[] { context });
      return returnValue;
      

      然后在我调用的方法中,我可以调用context.View("ViewName", model);。这可以有很多变体,基本思想是使用回调。

      【讨论】:

        【解决方案4】:

        另一种方法是使用装饰器:

        public class StatusCodeViewResultDecorator : ViewResult {
          ViewResult wrapped;
          int code;
          string description;
        
          public StatusCodeViewResultDecorator( ViewResult wrapped, int code, string description ) {
            this.wrapped = wrapped;
            this.code = code;
            this.description = description;
          }
        
          public override void ExecuteResult(ControllerContext context) {
            wrapped.ExecuteResult(context);
            context.RequestContext.HttpContext.Response.StatusCode = code;
            context.RequestContext.HttpContext.Response.StatusDescription = description;
          }
        }
        

        也许还有一种扩展方法使它更干净:

        public static class ViewResultExtensions {
          public static ViewResult WithStatus( this ViewResult viewResult, int code, string description ) {
            return new StatusCodeViewResultDecorator(viewResult,code,description);
          }
        }
        

        然后你可以简单地说:

        return View("MyView").WithStatus(404,"Not found");
        

        在您的控制器中。

        【讨论】:

        • 这并没有真正的帮助,因为您仍然必须在控制器中创建基本 ViewResult,这是我试图摆脱的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多