DELETE Method with signature IEnumerable<int> broken in RTM

Topics: ASP.NET Web API
Aug 16, 2012 at 6:24 PM

Previously Web API supported the web standards by allowing you to do a DELETE request like so:

http://localhost/api/task/delete?ID=XXX&ID=YYYY

combined with a method signature like this:

public void DELETE(IEnumerable<int> ID) { ...}

 

This allowed for batch delete per standards.

 

Now in RTM this no longer works and throws a bad request error. It is not a bad request, it is absolutely the standards compliant way to do batch delete with a DELETE request.

I believe this is a bug. Is there a work around?

Coordinator
Aug 16, 2012 at 11:25 PM

Hi Gemini,

Can you try adding the [FromUri] attribute to the parameter? Like this:

public void DELETE([FromUri] IEnumerable<int> ID) { ...}

By default in Web API a "complex" type (such as a list) is assumed to come from the body of the request and not the URI (such as the path or query string).

Thanks,

Eilon

Aug 16, 2012 at 11:47 PM

That works... however DELETE, according to the RFC for REST and HTTP is always URI and cannot contain a body at all.

In fact JQuery is actually fixing 1.9 and 2.0 so that it will properly put all parameters in the URI instead of the body (it's a bug in the current implementation of Jquery) because of the RFC docs that I provided them.

Further, most web servers will throw a hard error if DELETE has a body to it. (and Web API did so until RTM which was the correct behavior, the current one is not correct per the RFC).

My point to all of this is that [FromUri] on a HttpDelete is redundant as it's the only possible way and the parser should likely know that and only parse the URI and throw badrequest again if there is a body passed so that it's RFC standards compliant.

Coordinator
Aug 17, 2012 at 4:34 PM

Our current action selection and parameter binding logic is applied consistently to all actions independent of the HTTP method in question. We could try to specialize this logic for each HTTP method, but this would make the rules tricky to explain.

Aug 17, 2012 at 4:45 PM

I really don't think so...

GET clearly is only paramaters. It should only accept parameter and badrequest with a comment that get doesn't support a body when there is a body passed.

DELETE clearly only supports parameters. It should do the same as GET. 

This is how the RFC works and it would be unexpected for it to work any other way than the RFC, and given that others (mono) are now integrating the web stack in, this will cause major problems on other web servers other than IIS (I think IIS doesn't like deletes with body too, just not as bad as google and amazon hosting which will just throw bad request completely).

So I think its' entirely reasonable and with the proper error message being passed back with the badrequest result it's self-explanatory.

And given that v.next of jquery is going to pass this stuff properly as parameters on Delete I'd think since it's a huge portion of the people using web api, that you'd want it to be consistent.

Aug 17, 2012 at 4:59 PM

We want the ApiController programming model to be consistent across all HTTP methods. There are lots of inconsistent ways you can formulate an HTTP request but in general we don't check for that. The simple rule for action parameter binding is that "simple" types come from URI and "complex" types come from the body. If you want to change that then we provide attributes for your to do so.

Henrik

Aug 17, 2012 at 5:47 PM

My point is that by definition it won't be consistent across all methods nor should it be.

If you pass a body to a GET you'll get an error. Doesn't matter if it's a complex type or not because the web server will throw an error.

The RFC says the same thing about DELETE. A delete can only pass the Key of the item to delete and does so as parameters. DELETE cannot support complex types being sent to it. It can support an array of parameters in the URI by definition of passing multiple of the same parameter. But that's it.

By not enforcing the RFC you create inconsistency and FUD because you will get inconsistent results and worse you'll get inconsistent results between web servers using the same .NET code, not just between .NET and every other programming environment with the approach that you're using now. 

By throwing on deletes with bodies like GET should be if it isn't then you're consistent with the standards. Implicitly this also limits what the parameters can be on a GET and a DELETE method, as they should be limited. Only PUT and POST should ever accept complex types for obvious reasons and only in those two cases should the attribute have to be adorned if different.

Aug 17, 2012 at 6:57 PM

I understand your point but as I mentioned there are many inconsistencies in HTTP that we don't verify but that doesn't mean that we don't want a consistent way for people to decorate their controller actions. Further, you can actually have complex types coming out of the URI -- for example, JQuery encodes complex types using form URL-encoded data so it is not a black or white rule as to what can go where.

Henrik

Aug 17, 2012 at 8:53 PM

My point still stands. The way it works right now will ALWAYS fail on DELETE for anything other than basic parameters like an int or Guid.

One of the gapping holes in the samples and I think based on that your thinking in WebAPI is how to do batch operations.

DELETE is easy... except batch with DELETE right now in Web API doesn't work without a magical decorator which is neither discoverable nor something anyone has ever seen until .NET 4.5 because attributes on parameters on a method call is brand new and highly non-intuitive btw.

GET is batch by definition because it returns a batch. But it should also allow GET?ID=XXX,ID=YYY and it should also understand like DELETE so that you can pass an array of items to get. (Of course OData solves this, but the basic case still stands)

PUT AND POST both need to be able to do so, and the samples should show this. They work of course because you post a json array and it maps nicely to Ienumerable.

See the problem? The only one that doesn't work as expected out of the box is DELETE. The rest just work. DELETE does not. I fails and the only way to make it work is to put some unknown adornment it to tell it to process what is implicit based on the RFC for the HTTP transport spec.

So you have something that won't work and should work out of the box and requires a bizzare hack to make work what should work by default. 

Since MVC and Web API are convention over configuration, this decision is completely inconsistent with the entire platform. If I define delete, it should work according to the specs automatically without having to configure it.

Aug 20, 2012 at 2:51 AM

I appreciate your input but I think we have to accept that we disagree on the premise that the programming model for how to decorate action parameters should differ based on the HTTP method. IMO having this kind of "modal" API can lead to confusion as

  1. the rules are more complicated; and
  2. it is not clear what semantics applies to new concepts that are introduced later. Applying different semantics depending on the HTTP method only works if you know the set but HTTP methods are extensible and so it is not clear what the rules should be for those extensions.

As I mentioned, there also are problems in that parameters for any method can come as part of the request URI (any HTTP method can have a query component) so in general you have to be explicit where the data comes from.

Hope this helps,

Henrik

Aug 21, 2012 at 9:14 PM

And your approach ignores the RFC as is typical MS fracturing standards.

At the very least on GET and DELETE you should be automatically throwing BAD METHOD to force people to follow the standard and not hack things around. This is how MS always gets into trouble and gets people pissed off about standards compliancy. 

Aug 21, 2012 at 9:53 PM

Actually, my name is on RFC 2616 and every HTTP spec before that so I do consider myself familiar with HTTP. The issue has absolutely nothing to do with the RFC but purely is a programming model decision. There are any number of ways in which HTTP requests and responses can be semantically inconsistent but it is not the purpose of Web API to check these. Rather the purpose is to provide an intuitive, consistent way to expose data and functionality over HTTP. You may not agree with the choices we have made but there is absolutely nothing that goes against the grain of HTTP or otherwise affects the protocol.

Henrik

Aug 21, 2012 at 10:00 PM

Would you not agree that enforcing the RFC is providing an "intuitive, consistent way to expose data and functionality over HTTP?

Would you not agree that by not following the RFC and enforcing the rules that are clearly and unmistakably outlined in the documentation that you are actually doing the opposite? 

Would you not agree that if some developer comes along and hacks in a body to an DELETE function in code because WEB API allowed it and then another developer comes along and can't figure out what the heck's going on because of the fact that it's bad code and shouldn't be doing that would be be a problem? Wouldn't you agree that when MS sees the light and IIS starts rejecting GET and DELETE commands with bodies as a security volunerabiliy, that all of the Web API code that was allowed to be done in a non-standards compliant way that now breaks is going to cause problems and not be at all intuitive as to why it all of a sudden broke?

Would you not agree, that someone using Mono with the ASP.NET webstack in it hosted on Amazon, or Google or even most apache servers that won't allow a body in the DELETE AND GET per the RFC like you should be doing, would not find it intuitive to all of a sudden get an error where it worked before?

Would you not agree that it's a whole lot better to throw the error and tell people why in the error message NOW instead of people writting bad code that violates the standards and later having it blow up in their faces? If you agree that IE 6 was the spawn of the devil, then you'll agree with the above. IE 6's worst transgressions (other than not supporting left + right on position: absolute!) was that it allowed things to work that should have broken, which broke standards compatibility. Great for MS to keep market share, crap for developers.

Ultimately you'll do what you want to, but you should do the right thing and throw on a body of GET or DELETE per the standards so that people can't write bad code at all and you should tell people why in error message "BAD REQUEST: BODY NOT STANDARD COMPLIANT ON GET OR DELETE". Easy peasy and very intuitive.