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