2011-04-28 18 views
6

¿Es una buena práctica limitar los verbos HTTP disponibles para cada acción? Mi código es más limpio sin [HttpGet], [HttpPost], [HttpPut], o [HttpDelete] decorando cada acción, pero también podría ser menos robusto o seguro. No veo esto hecho en muchos tutoriales o códigos de ejemplo, a menos que el verbo sea explícitamente obligatorio, como tener dos acciones "Crear" donde la versión GET devuelve un nuevo formulario y la versión POST inserta un nuevo registro.Limitación de verbos HTTP en cada acción

+0

Tal vez un mejor enfoque sería al revés, creando Atributos personalizados que niegan los verbos que no quieres. Nunca lo intenté, simplemente diciendo :-) – goenning

Respuesta

3

Personalmente trato de respetar RESTful conventions y especificar el verbo HTTP a excepción de las acciones de GET que no modifican cualquier estado en el servidor por lo tanto permitiéndoles ser invocados con cualquier verbo HTTP.

0

No es necesario especificar el HttpGet, todos los demás sí es necesario

+0

¿Significa que HttpGet está allí por defecto si no se especifica? –

+1

No es una cuestión de "necesidad", sino "debería". Si se usa una acción para actualizar algo (por ejemplo, desde un POST AJAX), no * necesita * marcarlo con '[HttpPost]', pero parece que sería una buena idea. – MikeWyatt

1

Sí, creo que es una buena práctica limitar sus acciones solo al método HTTP apropiado que se supone debe manejar, esto mantendrá las solicitudes incorrectas fuera de su sistema, reducirá la efectividad de posibles ataques, mejorará la documentación de su código , aplicar un diseño de REST, etc.

Sí, utilizando el [HttpGet], [HttpPost] .. atributos pueden hacer su código más difícil de leer, especialmente si también está usando otros atributos como [OutputCache], [Authorize], etc.

utilizo un pequeño truco con un IActionInvoker personalizado, en lugar de usar atributos prefijo el método HTTP a th e acción nombre del método, por ejemplo:

public class AccountController : Controller { 

    protected override IActionInvoker CreateActionInvoker() { 
     return new HttpMethodPrefixedActionInvoker(); 
    } 

    public ActionResult GetLogOn() { 
     ... 
    } 

    public ActionResult PostLogOn(LogOnModel model, string returnUrl) { 
     ... 
    } 

    public ActionResult GetLogOff() { 
     ... 
    } 

    public ActionResult GetRegister() { 
     ... 
    } 

    public ActionResult PostRegister(RegisterModel model) { 
     ... 
    } 

    [Authorize] 
    public ActionResult GetChangePassword() { 
     ... 
    } 

    [Authorize] 
    public ActionResult PostChangePassword(ChangePasswordModel model) { 
     ... 
    } 

    public ActionResult GetChangePasswordSuccess() { 
     ... 
    } 
} 

Tenga en cuenta que esto no cambia los nombres de las acciones, que son todavía LogOn, LogOff, Register, etc.

Aquí está el código:

using System; 
using System.Collections.Generic; 
using System.Web.Mvc; 

public class HttpMethodPrefixedActionInvoker : ControllerActionInvoker { 

    protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName) { 

     var request = controllerContext.HttpContext.Request; 

     string httpMethod = request.GetHttpMethodOverride() 
     ?? request.HttpMethod; 

     // Implicit support for HEAD method. 
     // Decorate action with [HttpGet] if HEAD support is not wanted (e.g. action has side effects) 

     if (String.Equals(httpMethod, "HEAD", StringComparison.OrdinalIgnoreCase)) 
     httpMethod = "GET"; 

     string httpMethodAndActionName = httpMethod + actionName; 

     ActionDescriptor adescr = base.FindAction(controllerContext, controllerDescriptor, httpMethodAndActionName); 

     if (adescr != null) 
     adescr = new ActionDescriptorWrapper(adescr, actionName); 

     return adescr; 
    } 

    class ActionDescriptorWrapper : ActionDescriptor { 

     readonly ActionDescriptor wrapped; 
     readonly string realActionName; 

     public override string ActionName { 
     get { return realActionName; } 
     } 

     public override ControllerDescriptor ControllerDescriptor { 
     get { return wrapped.ControllerDescriptor; } 
     } 

     public override string UniqueId { 
     get { return wrapped.UniqueId; } 
     } 

     public ActionDescriptorWrapper(ActionDescriptor wrapped, string realActionName) { 

     this.wrapped = wrapped; 
     this.realActionName = realActionName; 
     } 

     public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters) { 
     return wrapped.Execute(controllerContext, parameters); 
     } 

     public override ParameterDescriptor[] GetParameters() { 
     return wrapped.GetParameters(); 
     } 

     public override object[] GetCustomAttributes(bool inherit) { 
     return wrapped.GetCustomAttributes(inherit); 
     } 

     public override object[] GetCustomAttributes(Type attributeType, bool inherit) { 
     return wrapped.GetCustomAttributes(attributeType, inherit); 
     } 

     public override bool Equals(object obj) { 
     return wrapped.Equals(obj); 
     } 

     public override int GetHashCode() { 
     return wrapped.GetHashCode(); 
     } 

     public override ICollection<ActionSelector> GetSelectors() { 
     return wrapped.GetSelectors(); 
     } 

     public override bool IsDefined(Type attributeType, bool inherit) { 
     return wrapped.IsDefined(attributeType, inherit); 
     } 

     public override string ToString() { 
     return wrapped.ToString(); 
     } 
    } 
} 
Cuestiones relacionadas