Создание ViewResults вне контроллеров в ASP.NET MVC - PullRequest
10 голосов
/ 22 февраля 2010

Некоторые из моих действий контроллера имеют стандартный набор поведения обработки ошибок. В общем, я хочу:

  • Загрузка объекта на основе данных маршрута (идентификаторов и т. П.)
    • Если данные маршрута не указывают на действительный объект (например, путем взлома URL), тогда сообщите пользователю о проблеме и верните HTTP 404 Not Found
  • Проверка того, что текущий пользователь имеет соответствующие разрешения на объект
    • Если у пользователя нет прав, сообщите ему о проблеме и верните HTTP 403 Запрещено
  • Если вышеприведенное успешно, то сделайте что-нибудь с этим объектом, специфичным для действия (т. Е. Сделайте его в виде). ​​

Эти шаги настолько стандартизированы, что я хочу иметь повторно используемый код для реализации поведения.

Мой текущий план атаки состоял в том, чтобы иметь вспомогательный метод, чтобы сделать что-то вроде этого:

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 вне определенного контроллера?

Ответы [ 4 ]

6 голосов
/ 13 декабря 2010

Просто прочитайте этот пост, поскольку у меня возникла та же проблема из фильтра действий. Моим решением было явное создание действия вида. Это основано на защищенном методе View () согласно источнику MVC, поэтому он должен заполнять необходимые свойства. Во всяком случае, похоже, работает без проблем.

public static NotFound(Controller controller){
    controller.HttpContext.Response.StatusCode = 404;

    ViewResult result = new ViewResult {
            ViewName = "NotFound",
            ViewData = controller.ViewData,
            TempData = controller.TempData
        };
    return result;
}

Немного поздно, но у меня это сработало.

4 голосов
/ 22 февраля 2010

Когда я писал это, я думал об одном способе.

Вместо того, чтобы иметь приведенный выше код в помощнике, я мог бы поместить его в подкласс Controller, а затем создать подкласс этого класса для моих реальных контроллеров.Это позволило бы мне вызывать защищенный метод View ().

Мне это особенно не нравится, потому что для требуется наследование для работы , но это все еще вариант.

2 голосов
/ 17 июля 2010

У меня был один и тот же вопрос, и я ответил на него по-разному. Я действительно не хотел использовать наследование для этого, поэтому вместо этого я использовал лямбду.

Во-первых, у меня есть объект, который я передаю от моего контроллера методу, которому я хочу вернуть представление:

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);. Вариантов может быть много, основная идея - использовать обратный вызов.

0 голосов
/ 18 мая 2010

Другой способ - использовать декораторы:

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");

в вашем контроллере.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...