views:

868

answers:

4

In ASP.NET MVC 2, a couple of new action filter attributes were introduced, as "shorthand" for attributes in ASP.NET MVC 1; for example, applying the HttpPostAttribute does the same thing as applying [AcceptVerbs(HttpVerbs.Post)] to an action method.

In addition, with the more verbose syntax, it is possible to combine different methods, in order to allow for example both Post and Delete.

Now I'm wondering: how do the new attributes work? If I apply both [HttpPost] and [HttpDelete], will ASP.NET MVC 2 allow both or require both (thus allowing nothing)?

A: 

If you put [HttpPost] and [HttpDelete] together it will require both (which isn't possible) I think. If you chain [HttpGet] it won't work either, etc...

You can easily test it my just taking an existing [HttpPost] action method and adding [HttpDelete] to it. The post will stop working.

I haven't found any examples where I would need to chain them like your suggesting though.

Kelsey
The reason I'm asking is that I would like to allow *both* POST and DELETE requests, but no other. I had hoped that the attributes would work like the UNION set operator, rather than the INTERSECTION operator... But well, you can't always get what you want. Do you have a source on this behavior somewhere?
Tomas Lycken
HttpPost and HttpDelete is pretty commonI would expect if you expect a delete action to operate both using AJAX (Delete) and non-AJAX (Post), when javascript is disabled.
tvanfosson
I actually tried a test and couldn't find any combinations where I could get a chained attribute like your example to work. I took working methods and just added attributes to them and sure enough it broke them all.
Kelsey
+1  A: 

Looking at the code for ActionMethodSelector, it appears that all action method attributes must return true for IsValidForRequest before that action will be added to the set of possible matching methods. Since it's not possible for HttpPost and HttpDelete to return IsValidForRequest for the same request, I would expect that using both will prevent that action from ever matching any request.

Here's a telling comment from the code:

private static List RunSelectionFilters(...) {
// remove all methods which are opting out of this request
// to opt out, at least one attribute defined on the method must return false

(emphasis mine)

Note that you can still use AcceptVerbs and explicitly OR the verbs if you need to match either.

EDIT -- here's an HttpPostOrDelete attribute for you.

[AttributeUsage( AttributeTargets.Method, AllowMultiple = false, Inherited = false )]
public class HttpPostOrDeleteAttribute : ActionMethodSelectorAttribute
{
    private static readonly AcceptVerbsAttribute _innerPostAttribute = new AcceptVerbsAttribute( HttpVerbs.Post );
    private static readonly AcceptVerbsAttribute _innerDeleteAttribute = new AcceptVerbsAttribute( HttpVerbs.Delete );

    public override bool IsValidForRequest( ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo )
    {
        return _innerDeleteAttribute.IsValidForRequest( controllerContext, methodInfo )
               || _innerPostAttribute.IsValidForRequest( controllerContext, methodInfo );
    }
}
tvanfosson
I know I still *can* use `AcceptVerbs`, but I think the new attributes look a lot nicer in the code, and had hoped the MVC team had thought about this when implementing them...
Tomas Lycken
@Tomas -- fortunately, you can make your own as needed. See my update.
tvanfosson
+1  A: 

All filters in MVC are - without exception - independent of one another. No filter is special-cased anywhere in the MVC framework. This was an intentional design decision so that MVC framework components like the invoker can't "cheat" and treat filters located in the MVC binary any differently than filters that you as an application developer would have written.

So when the invoker sees [HttpGet] and [HttpPost] on the same method, there's no special-casing code to take the union of the two. They're executed independently. And since they can never return true for the same request, [HttpGet, HttpPost] effectively excludes any particular method from being an action method.

Levi
A: 

You can use AcceptVerbs for chaining, e.g.:

[AcceptVerbs(HttpVerbs.Get|HttpVerbs.Post)]
public ActionResult Customers() {
}

or

[AcceptVerbs("GET","POST")]
public ActionResult Customers() {
}
Ankur Bulsara