ASP.NET MVC 3 Gotcha (Bug?): Different parameter binding priority for GET and POST
Given this route:
routes.MapRoute("home", "{action}/{id}",
new { controller = "home", action = "index", id = UrlParameter.Optional });
...and this action:
public ActionResult Hi(string id) {
return Content("hello, id: " + id);
}
Question #1 What is the response for:
GET http://localhost:2247/hi/7?id=55 HTTP/1.1
Question #开发者_开发技巧2 What is the response for:
POST http://localhost:2247/hi/7?id=55 HTTP/1.1
Content-Length: 4
Content-Type: application/x-www-form-urlencoded
id=3
I believe this is a bug and the route value should always have precedence, since the URL is what identifies a resource. If you write a POST, PUT or DELETE action you expect the id coming from the URL, not from the body of the request. This could cause changes to a different resource, and can be exploited by malicious users.
After doing some research found that the issue is the default ValueProviderFactory registration order, where FormValueProviderFactory comes before RouteDataValueProviderFactory. Instead of messing with the order I created a 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;
}
}
...which you can use like this:
public ActionResult Hi([FromRoute]string id) {
return Content("hello, id: " + id);
}
In ASP.NET MVC 3 RC2:
- GET: Response: hello, id: 7
- POST: Response: hello, id: 3
And here's the test view:
<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>
So here is the evaluation precedence order:
- POST body parameter
- Route
- Query string parameter
And by the way the exact same result is obtained with ASP.NET MVC 2.0.
I would guess that for the GET the response would be "Hello 7" and for the POST would be "Hello 3".
精彩评论