ASP.NET MVC 3 Gotcha(Bug?):GET和POST的不同参数绑定优先级

Max*_*oro 4 asp.net asp.net-mvc asp.net-mvc-3

鉴于这条路线:

routes.MapRoute("home", "{action}/{id}",
  new { controller = "home", action = "index", id = UrlParameter.Optional });
Run Code Online (Sandbox Code Playgroud)

......这个动作:

public ActionResult Hi(string id) {
   return Content("hello, id: " + id);
}
Run Code Online (Sandbox Code Playgroud)

问题#1 回复是什么:

GET http://localhost:2247/hi/7?id=55 HTTP/1.1
Run Code Online (Sandbox Code Playgroud)

问题#2 回复是什么:

POST http://localhost:2247/hi/7?id=55 HTTP/1.1
Content-Length: 4
Content-Type: application/x-www-form-urlencoded

id=3
Run Code Online (Sandbox Code Playgroud)


我相信这是一个错误,路由值应该始终具有优先权,因为URL是标识资源的.如果您编写POST,PUT或DELETE操作,您希望ID来自URL,而不是来自请求正文.这可能会导致对不同资源的更改,并可被恶意用户利用.
在做了一些研究后发现问题是默认的ValueProviderFactory注册顺序,其中FormValueProviderFactory位于RouteDataValueProviderFactory之前.我没有搞乱订单,而是创建了CustomModelBinderAttribute:

[AttributeUsage(AttributeTargets.Parameter)]
public sealed class FromRouteAttribute : CustomModelBinderAttribute, IModelBinder {

   public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {

      bindingContext.ValueProvider = new RouteDataValueProvider(controllerContext);

      return ModelBinders.Binders.DefaultBinder.BindModel(controllerContext, bindingContext);
   }

   public override IModelBinder GetBinder() {
      return this;
   }
}
Run Code Online (Sandbox Code Playgroud)

...你可以像这样使用:

public ActionResult Hi([FromRoute]string id) {
   return Content("hello, id: " + id);
}
Run Code Online (Sandbox Code Playgroud)

Dar*_*rov 6

在ASP.NET MVC 3 RC2中:

  • GET:回复:你好,id:7
  • POST:响应:你好,id:3

这是测试视图:

<a href="/hi/7?id=55">GET</a>
<form action="/hi/7?id=55" method="POST">
    <input type="hidden" name="id" value="3" />
    <input type="submit" value="POST" />
</form>
Run Code Online (Sandbox Code Playgroud)

所以这是评估优先顺序:

  1. POST body参数
  2. 路线
  3. 查询字符串参数

顺便说一下,使用ASP.NET MVC 2.0可以获得完全相同的结果.