2010-02-22 15 views
10

Varias de mis acciones de controlador tienen un conjunto estándar de comportamiento de manejo de fallas. En general, quiero:Creación de ViewResults fuera de los controladores en ASP.NET MVC

  • Cargar un objeto basado en los datos de ruta (identificadores y similares)
    • Si los datos de ruta no apunta a un objeto válido (por ejemplo: a través de la URL piratería) a continuación, informar al usuario del problema y devolver un HTTP 404 no encontrado
  • validar que el usuario actual tiene los permisos adecuados en el objeto
    • Si el usuario no tiene permiso, informar al usuario del problema y devolver un HTTP 403 Prohibido
  • Si lo anterior es exitoso, entonces haga algo con ese objeto que sea específico de la acción (es decir: renderízalo en una vista).

Estos pasos están tan estandarizados que quiero tener código reutilizable para implementar el comportamiento.

Mi plan actual de ataque era tener un método de ayuda a hacer algo como esto:

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; 
} 

El problema aquí es que Controller.View() es un método protegido y por lo tanto no es accesible desde un ayudante . He considerado crear una nueva instancia de ViewResult explícitamente, pero hay suficientes propiedades para establecer que soy cauteloso al hacerlo sin conocer las trampas primero.

¿Cuál es la mejor manera de crear un ViewResult desde fuera de un controlador en particular?

Respuesta

4

Mientras escribía esto pensé de una manera.

En lugar de tener el código anterior en un helper, podría ponerlo en una subclase de Controller y luego subclasificar esta clase para mis controladores reales. Esto me permitiría llamar al método View() protegido.

No me gusta particularmente, porque requires inheritance to work, pero sigue siendo una opción.

+0

+1: Así es como lo haría. –

+0

Lo que no me gusta de esto es que significa que un controlador solo puede tener un conjunto de comportamiento de acción compartido: el que hereda (solo). Para este ejemplo en particular, no es un problema, pero puedo ver que se vuelve mucho más difícil a medida que el proyecto crece. –

+0

Me di cuenta de que su pregunta tiene años, pero pensé que mencionaría otra posibilidad. Podría exponer una versión pública de 'View()' llamada 'GetView()' en su controlador base, luego podría mantener su ayudante en una clase separada. Todavía necesitarías pasar tu controlador a tu ayudante, lo cual es un poco incómodo, pero permite la composición en lugar de la herencia. – devuxer

0

Otra manera sería utilizar decoradores:

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; 
    } 
} 

y tal vez un método de extensión para que sea más limpia:

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

A continuación, puede decir simplemente:

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

en su controlador.

+1

Esto realmente no ayuda, porque todavía tiene que crear el ViewResult base en el controlador, que es lo que estoy tratando de evitar. –

2

Tuve la misma pregunta y la contesté de manera diferente. Realmente no quería usar herencia para esto, así que usé una lambda en su lugar.

En primer lugar, Tengo un objeto que yo paso de mi controlador al método Quiero volver la vista:

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

se crea una instancia de esta y pasarlo como parámetro al método que se devolver el resultado:

// 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; 

Luego, en el método invoqué, me puede llamar context.View("ViewName", model);. Puede haber muchas variaciones de esto, la idea básica es usar la devolución de llamada.

6

Acabo de leer esta publicación porque tenía el mismo problema de un filtro de acción. Mi solución fue crear la acción de vista de forma explícita. Esto se basa en el método View() protegido según la fuente MVC, por lo que debe llenar las propiedades requeridas. De todos modos, parece funcionar sin problemas.

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

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

Un poco tarde en el día pero esto funcionó para mí.

Cuestiones relacionadas