Asp.net mvc 限制每个操作上的HTTP谓词

Asp.net mvc 限制每个操作上的HTTP谓词,asp.net-mvc,httpverbs,Asp.net Mvc,Httpverbs,限制每个操作的可用HTTP谓词是否是一种好的做法?我的代码更干净,没有[HttpGet]、[HttpPost]、[HttpPut]或[HttpDelete]修饰每个操作,但它也可能不够健壮或安全。我在很多教程或示例代码中都看不到这一点,除非动词是明确要求的,比如有两个“创建”操作,其中GET版本返回一个新表单,POST版本插入一个新记录。您不需要指定HttpGet,您确实需要的所有其他操作我个人尝试尊重并指定HTTP谓词,但GET操作除外,该操作不会修改服务器上的任何状态,因此允许使用任何HTT

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

您不需要指定HttpGet,您确实需要的所有其他操作

我个人尝试尊重并指定HTTP谓词,但GET操作除外,该操作不会修改服务器上的任何状态,因此允许使用任何HTTP谓词调用它们。

是的,我相信将您的操作限制为它应该处理的适当HTTP方法是一种很好的做法,这将使坏请求远离您的系统,降低可能的攻击的有效性,改进代码的文档,实施RESTful设计,等等

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

我对自定义的
iactionvoker
使用了一个小技巧,而不是使用属性,我在操作方法名称前面加上了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() {
      ...
   }
}
请注意,这不会更改操作名称,这些名称仍然是
登录
注销
注册
,等等

代码如下:

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();
      }
   }
}
使用系统;
使用System.Collections.Generic;
使用System.Web.Mvc;
公共类HttpMethodPrefixedActionInvoker:ControllerActionInvoker{
受保护的覆盖操作描述符查找操作(ControllerContext ControllerContext、ControllerDescriptor ControllerDescriptor、string actionName){
var request=controllerContext.HttpContext.request;
字符串httpMethod=request.gethttpmethodverride()
??request.HttpMethod;
//对HEAD方法的隐式支持。
//如果不需要头部支撑,则使用[HttpGet]装饰动作(例如,动作有副作用)
if(String.Equals(httpMethod,“HEAD”,StringComparison.ordinallingorecase))
httpMethod=“GET”;
字符串httpMethodAndActionName=httpMethod+actionName;
ActionDescriptor adescr=base.FindAction(controllerContext、controllerDescriptor、httpMethodAndActionName);
if(adescr!=null)
adescr=新的ActionDescriptorRapper(adescr,actionName);
返回adescr;
}
类ActionDescriptorRapper:ActionDescriptor{
只读操作描述符包装;
只读字符串realActionName;
公共重写字符串ActionName{
获取{return realacionname;}
}
公共覆盖控制器描述器控制器描述器{
获取{return wrapped.ControllerDescriptor;}
}
公共重写字符串唯一ID{
获取{return wrapped.UniqueId;}
}
公共ActionDescriptorRapper(ActionDescriptor包装,字符串realActionName){
this.wrapped=wrapped;
this.realActionName=realActionName;
}
公共重写对象执行(ControllerContext ControllerContext,IDictionary参数){
返回wrapped.Execute(controllerContext,参数);
}
公共重写参数描述符[]GetParameters(){
返回wrapped.GetParameters();
}
公共覆盖对象[]GetCustomAttributes(布尔继承){
返回wrapped.GetCustomAttributes(继承);
}
公共重写对象[]GetCustomAttributes(类型attributeType,布尔继承){
返回wrapped.GetCustomAttributes(attributeType,inherit);
}
公共覆盖布尔等于(对象对象对象){
返回包装。等于(obj);
}
公共覆盖int GetHashCode(){
返回wrapped.GetHashCode();
}
公共覆盖ICollection GetSelectors(){
返回wrapped.GetSelectors();
}
已定义公共重写bool(类型attributeType,bool inherit){
返回wrapped.IsDefined(attributeType,inherit);
}
公共重写字符串ToString(){
return wrapped.ToString();
}
}
}

也许另一种方法更好,创建自定义属性来拒绝不需要的动词。从未尝试过,只是说:-)如果没有指定,是否意味着默认情况下HttpGet存在?这不是“需要”的问题,而是“应该”的问题。如果某个操作用于更新某些内容(例如,来自AJAX帖子),则不需要使用
[HttpPost]
标记它,但这似乎是一个好主意。