Несколько из моих действий контроллера имеют стандартный набор обрабатывающего отказ поведения. В целом я хочу:
Эти шаги так стандартизированы, что я хочу иметь повторно используемый код для реализации поведения.
Мой текущий план нападения состоял в том, чтобы иметь вспомогательный метод сделать что-то вроде этого:
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;
}
Проблемой здесь является тот Контроллер. Представление () является защищенным методом и так не доступно от помощника. Я посмотрел на создание нового экземпляра ViewResult явно, но существует достаточно свойств для установки этого, я осторожен о выполнении так, не зная ловушки сначала.
Что лучший способ состоит в том, чтобы создать ViewResult снаружи конкретного Контроллера?
Когда я писал это, я придумал один способ.
Вместо того, чтобы иметь приведенный выше код в помощнике, я мог бы поместить его в подкласс Controller, а затем создать подкласс этого класса для моих реальных контроллеров. Это позволило бы мне вызвать защищенный метод View ().
Мне это не очень нравится, потому что для работы требуется наследование , но это все же вариант.
Другим способом было бы использование декораторов:
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");
в своем контроллере.
У меня был тот же вопрос, но я ответил по-разному. Я действительно не хотел использовать для этого наследование, поэтому вместо этого использовал лямбду.
Во-первых, у меня есть объект, который я передаю от моего контроллера методу, который я хочу вернуть в виде:
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);
. Может быть много вариантов, основная идея - использовать обратный вызов.