1 vote
closed
AcceptVerbAttribute Not Following HTTP Standards

Description

 
According to HTTP Standards when a HTTP method isn't allowed a status code of 405 Method Not Allowed is suppose to be returned. That is not happening with the AcceptVerbAttribute. It is being used to skip a route even if the route is valid, and move on to processing the other routes. Which may or may not be found later on. This will result in a 404 if the route is not found, which is not the desired action.

For example I have the following route that only accepts POST.

/customer/info

It should be processed in the following way according to HTTP standards for the following verbs

GET = 405 Method Not Allowed
POST = 200 OK
PUT = 405 Method Not Allowed
...

However this is what happens with the current framework if there is no generic route to catch it

GET = 404 Not Found
POST = 200 OK
PUT = 404 Not Found
...

This is a problem because 404 Not Found is not HTTP Method specific, it means that the URL is Not Found, not the URL + Verb is Not Found. Is there anyway to fix this problem?

File Attachments


No files are attached


Closed Aug 30 2008 at 1:06 AM  by Haacked

Comments

most recent at top (show oldest at top)
pierslawson wrote Oct 29 2008 at 9:37 PM
With the Beta vesrion it is possible to have Action methods that only accept your supported verbs then a catch all to get the rest. In your catch all you can return a 405. I'm working through a big example on my blog: http://shouldersofgiants.co.uk/Blog

nberardi wrote Sep 25 2008 at 10:10 PM
auriel, the big problem with the way it is currently being done is that it doesn't allow a nice REST interface to be created. If I have the interface which changes based on the VERB, and the VERB hasn't been reconciled then it keeps on going through the routes to process the rest of them instead of bombing out on the specific set of actions that handle it. To complicate things there are 8 different VERB's that we have to account for if we want the interface to respond correctly.

OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT

These are all valid, so if I want my REST interface to say bad request or stop for anything that is not GET, PUT, or DELETE, then I need to create dummy actions for HEAD, POST, OPTIONS, TRACE, and CONNECT. This doesn't seem like efficient programming.

The problem is I guess is that there is no way to treat a single ActionName as one group they each have to be treated individually and this causes problems when creating RESTful interfaces. It works great for Forms where the HTML is the interface, but works very poorly when the URL is the interface.

auriel wrote Sep 25 2008 at 6:06 PM
Yes, we're aware that this is technically in violation of the HTTP standard. However,

(a) When is the last time you actually saw *any* framework return an HTTP 405 in response to a request?, and
(b) It would be very, very difficult for us to know when it is correct to return an HTTP 405. All filters operate independently of each other, and we made the decision that the invoker should be agnostic so that it doesn't special-case *any* type of filter. As a result, we don't know why method resolution fails for any particular action method, so we wouldn't know when it's appropriate for us to return an HTTP 405.

Given those, it was simply not worth it to generate an HTTP 405 in response to a request that couldn't be matched solely on the basis of the HTTP verb. If your application is actually *breaking* as a result of this decision, please let us know so that we can consider reopening this bug. Otherwise, we feel our time is better spent on other features and functionality.

jarrettv wrote Sep 25 2008 at 4:47 PM
I agree that this is but should be re-opened.

nberardi wrote Sep 2 2008 at 8:41 PM
Just to re clarify, probably won't be enough to reopen this bug, but I need to make my peace.

HTTP standard is very specific about what you are suppose to do if the URL matches but the verb doesn't. It says you are suppose to throw a 405 Method Not Found.

I know I don't have to use AcceptVerbAttribute, but I really think the ActionNameAttribute is a killer feature, and I would love to use it. But both ActionName and AcceptVerb seem to be strongly tied together. Almost enough that they should probably be in the same attribute.

Such as:
[ActionName(Verb="GET")]
[ActionName("MyAction", Verb="POST")]

Haacked wrote Aug 30 2008 at 12:51 AM
AcceptVerbs is not an authorization filter, it's an action method selection attribute. http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx
If you really want to say that a verb is not allowed, you can do so in Routing by specifying an HttpMethodConstraint.

Phil

nberardi wrote Aug 29 2008 at 9:15 PM
Auriel,

I understand that currently each AcceptVerb operates separate from all others in the current API. And has no knowledge of any of the others. My request for the AcceptVerb to operate with in a certain ActionName, was more of a best practices/standard compliant request. Because I can think of many reasons why you would want a certain sub-section of processors with in a certain ActionName. For example I might want my REST interface to only accept GET, PUT, and DELETE, however I want the POST to be actively rejected as not valid, I don't want one of my other routes below it catching it and trying to serve a value.

To do this in the current framework I would need something like:

[AcceptVerb("GET")] -> return GoodResult();
[AcceptVerb("PUT")] -> return GoodResult();
[AcceptVerb("DELETE")] -> return GoodResult();
[AcceptVerb("POST", "TRACE", "HEAD", ...)] -> return MethodNotFountResult();

So in order to satisfy my pretty common requirement when dealing with REST I have to head off all the other VERBS and return a dummy result that throws the 405 error.

Really in the perfect world I would like to see the AcceptVerb contained with in a single ActionName, so that the results can be correctly thrown when in error.

But I realize we are in alpha and things are changing pretty fast, so really this is more of a good practices criticism than anything else.

auriel wrote Aug 29 2008 at 8:40 PM
I see what you mean with the GET/POST/PUT example, but understand that each [AcceptVerbs] attribute operates independently of the others. Consider your first example, copied below:

[AcceptVerb("GET")]
public ActionResult MyAction(...);
[ActionName("MyAction"), AcceptVerb("POST")]
public ActionResult MyAction_FormSubmit(...);

If a GET request comes in for "MyAction", we're going to query both the GET and the POST attributes, asking if they can handle the current request. The semantic you'd want here is that the GET attribute says "yes" and the POST attribute says "no". If instead the GET attribute says "yes" and the POST attribute throws an HTTP 405, the entire request ends up as an HTTP 405. The POST attribute has no way of knowing that there actually exists a method that *can* handle the current request.

In your second scenario, copied below:

[AcceptVerb("GET")]
public ActionResult MyAction(...);
[ActionName("MyAction"), AcceptVerb("GET")]
public ActionResult MyAction_FormSubmit(...);

It's true that this is perfectly valid from the compiler's point of view, but the invoker throws an exception if it encounters something like this, since now both MyAction() and MyAction_FormSubmit() have said that they can handle the current request, and we've encountered an ambiguity.

Please let me know if this is still unclear or if I'm just misunderstanding your example. Thanks for your input! :)

nberardi wrote Aug 29 2008 at 8:20 PM
So if I had the following code from the release notes:

[AcceptVerb("GET")]
public ActionResult MyAction(...);
[ActionName("MyAction"), AcceptVerb("POST")]
public ActionResult MyAction_FormSubmit(...);

GET on MyAction = 200 OK
POST on MyAction = 200 OK
PUT on MyAction = 405 Method Not Allowed

So you check the verbs inside of the ActionName. Also what is the expected outcome of the following:

[AcceptVerb("GET")]
public ActionResult MyAction(...);
[ActionName("MyAction"), AcceptVerb("GET")]
public ActionResult MyAction_FormSubmit(...);

Which would essentially create two interfaces that are the same, and would not be caught by the compiler.

nberardi wrote Aug 29 2008 at 8:15 PM
Hi Auriel,

I see the definite use of AcceptVerb when combined with a specific ActionName. However AcceptVerb should only be processed inside of an ActionName. So if a certain ActionName only allows the POST verb. Or if there are 3 action methods with the same ActionName for POST, GET, & DELETE. If any other VERB is found then a 405 should be thrown for that ActionName. Because the URL has to be treated as a constant against a certain action name. When it is not you will be essentially getting different interfaces based in possible different controllers depending on the VERB. I could easily see an instance where a developer has put only a AcceptVerb("GET") on an action. Then if somebody does a POST to that action it might be caught by some generic controller action combo and cause at best a 500 error and at worst a security problem.

auriel wrote Aug 29 2008 at 6:45 PM
As an aside, if you want HTTP 405 semantics for your application, you could write a custom selector similar to the one we provided with [AcceptVerb]. The difference would be that instead of returning false if the selector doesn't match, you would instead throw an HTTP 405. The downside to this is that you wouldn't be able to use this on an overloaded method or in conjunction with the [ActionName] attribute, but aside from that this should work.

auriel wrote Aug 29 2008 at 6:40 PM
This is by design. There's presently no good way for a route to say "keep trying every other route you come across, but if you don't find anything throw a 405." Each route must make a binary decision when asked, and because of this binary decision it's very difficult to make a route know when it's appropriate to throw an HTTP 405.

In the particular example presented by this bug, we're using action method selectors instead of routes, but the limitation is the same.

Updating...
© 2006-2010 Microsoft | About CodePlex | Privacy Statement | Terms of Use | Code of Conduct | Advertise With Us | Version 2010.1.12.16187