限制每个操作的HTTP谓词

时间:2011-04-28 14:29:32

标签: asp.net-mvc httpverbs

限制每次操作的可用HTTP谓词是一种好习惯吗?我的代码更清晰,没有[HttpGet][HttpPost][HttpPut][HttpDelete]装饰每个操作,但它可能也不那么健壮或安全。我没有在许多教程或示例代码中看到这一点,除非明确要求动词,例如有两个“创建”动作,其中GET版本返回一个新表单,POST版本插入一个新记录。

3 个答案:

答案 0 :(得分:3)

我个人尝试尊重RESTful conventions并指定HTTP动词,但GET动作除外,它不会修改服务器上的任何状态,从而允许用任何HTTP动词调用它们。

答案 1 :(得分:1)

是的,我认为将您的操作仅限于它应该处理的相应HTTP方法是一种很好的做法,这会将不良请求保留在系统之外,降低可能的攻击效率,改进代码文档,执行RESTful设计等。

是的,使用[HttpGet][HttpPost] ..属性可能会使您的代码更难阅读,特别是如果您还使用其他属性,例如[OutputCache][Authorize]

我使用自定义IActionInvoker的小技巧,而不是使用属性我将HTTP方法添加到操作方法名称,例如:

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

请注意,这不会更改仍为LogOnLogOffRegister等的操作名称。

以下是代码:

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

答案 2 :(得分:0)

您无需指定HttpGet,您需要的其他所有