2011-04-28 4 views
6

Est-ce une bonne pratique de limiter les verbes HTTP disponibles pour à chaque action? Mon code est plus propre sans [HttpGet], [HttpPost], [HttpPut], ou [HttpDelete] décorant chaque action, mais il pourrait aussi être moins robuste ou sécurisé. Je ne vois pas cela dans de nombreux didacticiels ou exemples de code, sauf si le verbe est explicitement requis, comme avoir deux actions "Create" où la version GET renvoie un nouveau formulaire et la version POST insère un nouvel enregistrement.Limitation des verbes HTTP sur chaque action

+0

Peut-être une meilleure approche serait l'inverse, la création d'attributs personnalisés qui nient les verbes que vous ne voulez pas. Jamais essayé, juste dire :-) – goenning

Répondre

3

Personnellement, j'essaie de respecter RESTful conventions et spécifiez le verbe HTTP GET à l'exception des actions qui ne modifient pas l'état sur le serveur ainsi leur permettant d'être invoqué avec n'importe quel verbe HTTP.

0

Vous n'avez pas besoin de spécifier le HttpGet, tous les autres que vous avez besoin

+0

Cela signifie-t-il que HttpGet est là par défaut s'il n'est pas spécifié? –

+1

Ce n'est pas une question de "besoin", mais "devrait". Si une action est utilisée pour mettre à jour quelque chose (par exemple à partir d'un AJAX POST), vous n'avez pas * besoin * de le marquer avec [HttpPost] ', mais il semble que ce soit une bonne idée. – MikeWyatt

1

Oui, je crois que c'est une bonne pratique de limiter vos actions uniquement à la méthode HTTP appropriée, cela permettra de garder les mauvaises requêtes hors de votre système, de réduire l'efficacité des attaques possibles, d'améliorer la documentation de votre code , appliquer une conception RESTful, etc.

Oui, en utilisant le [HttpGet], [HttpPost] .. attributs peuvent rendre votre code plus difficile à lire, surtout si vous utilisez également d'autres attributs comme [OutputCache], [Authorize], etc.

J'utilise un petit truc avec un IActionInvoker personnalisé, au lieu d'utiliser des attributs, je préfixer la méthode HTTP à la Nom de la méthode d'action e, par exemple:

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() { 
     ... 
    } 
} 

Notez que cela ne change pas les noms d'action, qui sont encore LogOn, LogOff, Register, etc.

Voici le code:

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(); 
     } 
    } 
} 
Questions connexes