
IE9 deletes stuff - srozet
http://techno-weenie.net/2011/8/19/ie9-deletes-stuff/
======
patio11
I think this is more Rails encouraging somewhat sloppy use of return values,
since Rails (and the community) claim to be kind of slavish about REST-y code
but in practice tend to template off of well-known Rails-y code samples rather
than thinking "What am I really doing here?"

Rails _happens to function_ under browsers popular at development time, but
what do the standards (see nbpoole's comment) or logic suggest is the correct
response for this? Asking the user agent (which in this case essentially means
Javascript running on a page the user is browsing) whether /widgets/32 is
equivalent to /widgets/ so it should know whether it should nuke it or not?
That's a tough question for Javascript and an impossible question for the
actual human who is trying to get this year's widget catalog ready.

"User requested a delete, server doesn't say delete is impossible, we'll try
deleting until we get back confirmation or get told it is impossible" seems
like a fairly sensible decision for me. (It is non-compliant with the specs,
but GETing the new URL is _also_ non-compliant with the specs, and that is the
non-compliance that _Rails relies on to actually function_.)

There were similar issues way back in the day back when the Rails community
was enamored with redirect_to prior to finding out important constituents on
the Internet like, say, Googlebot think there are hugely meaningful
differences between 301 redirects and 302 redirects.

~~~
technoweenie
Sure, the code is sloppy. For whatever reason, that action wasn't using
"newer" rails practices to return different responses to form and ajax
requests.

    
    
        respond_to do |format|
          format.js   { head 200 }
          format.xml  { head 200 }
          format.html { redirect_to '/' }
        end
    

We do something like this in every other case in the code base. This is
actually what's more encouraged to Rails developers.

There was another similar issue with Rails and the Google Web Accelerator:
[http://37signals.com/svn/archives2/google_web_accelerator_he...](http://37signals.com/svn/archives2/google_web_accelerator_hey_not_so_fast_an_alert_for_web_app_designers.php).
In this case, the GWA was "clicking" links on the page to pre cache them.
Sucks to be you if your application's delete links are straight GET links!

In this situation, the community encountered the problem and immediately
started advocating better practices to get around these problems. Such as
using form buttons (and later, ajax-enabled links) for unsafe actions. There's
a clear standard practice in the web development community here, and these
early Rails apps weren't following it.

However, every other browser with XHR support for years has handled redirects
the same way. Even though the spec is ambiguous, it certainly suggests being
cautious and only automatically redirecting with safe HTTP methods. IE9 breaks
this trend for some reason. I'd feel much differently about this if every
version of IE worked this way. At least there would be some consistency with
itself. And it surely would've been encountered and added to the encyclopedia
of weird IE behavior that web developers have to deal with.

------
nbpoole
> _This works great in all modern browsers, except IE9. We discovered that not
> only does IE9 send a real DELETE request, it also follows the redirect with
> another DELETE. If that redirect points to another resource, you can get a
> dangerous cascading effect._

redirect_to issues a 302 redirect. According to
<http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html>, this is what a 302
redirect means:

 _The requested resource resides temporarily under a different URI. Since the
redirection might be altered on occasion, the client SHOULD continue to use
the Request-URI for future requests. This response is only cacheable if
indicated by a Cache-Control or Expires header field._

 _The temporary URI SHOULD be given by the Location field in the response.
Unless the request method was HEAD, the entity of the response SHOULD contain
a short hypertext note with a hyperlink to the new URI(s)._

 _If the 302 status code is received in response to a request other than GET
or HEAD, the user agent MUST NOT automatically redirect the request unless it
can be confirmed by the user, since this might change the conditions under
which the request was issued._

 _Note: RFC 1945 and RFC 2068 specify that the client is not allowed to change
the method on the redirected request. However, most existing user agent
implementations treat 302 as if it were a 303 response, performing a GET on
the Location field-value regardless of the original request method. The status
codes 303 and 307 have been added for servers that wish to make unambiguously
clear which kind of reaction is expected of the client._

There was another discussion about the use of 302 versus 303 redirects on HN
about a month ago: <http://news.ycombinator.com/item?id=2791663>

~~~
jsdalton
Good find. I was about to post a link to that very same discussion, because
the same thing crossed my mind.

Interestingly there was a lot of negativity in that discussion about the
author's idea. Criticism was mainly along the lines of "it's too risky" and
"it won't work with old browser" along with "the old way works fine, I don't
get what the big deal is."

Well, now we have at least one very _good_ reason to pay closer attention to
the spec and consider that the use of 303 may be more appropriate.

------
pilif
It's funny - about a month ago there was a discussion here that you should be
using the 303 redirect in response to a post.

The discussion was here: <http://news.ycombinator.com/item?id=2791663>

In there, I commented (<http://news.ycombinator.com/item?id=2792357>) that I
would recommend against 303 because of possibly broken proxies and mainly
because browser were already doing the right thing in response to a 302.

How wrong I was as we now have an instance where at least one browser is doing
it the way the spec has probably intended for it to be handled.

I would assume they are still handling 302 in response to a POST like every
other browser but for 303 they are doing it the way the spec was initially
worded.

This is, IMHO, not an IE issue but an IE standards-compliancy-feature at least
until somebody here tries whether it works as expected with a 303 status code.
If it does and you want it to work differently with a 302 code, then it's
probably your turn to get the spec fixed :-)

------
EricLaw-MSFT
Please see
[http://blogs.msdn.com/b/ieinternals/archive/2011/08/19/under...](http://blogs.msdn.com/b/ieinternals/archive/2011/08/19/understanding-
the-impact-of-redirect-response-status-codes-on-http-methods-like-head-get-
post-and-delete.aspx) for a complete discussion of behavior.

~~~
ldar15
Its funny that you can trot out the standard in your defense, cut and pasting
a relavent paragraph, yet seem unable to read the _immediately preceding
paragraph_ of the spec:

 _If the 302 status code is received in response to a request using the POST
method, the user agent_ must not automatically redirect* the request unless it
can be confirmed by the user, since this might change the conditions under
which the request was issued.*

<http://www.ietf.org/rfc/rfc1945.txt>

IE _does_ redirect automatically. So IE isn't doing it right either. Since
nobody is doing it right, I'd pick the guys doing it wrong that make the most
sense. What a surprise: that's not microsoft.

~~~
strmpnk
I would wonder what the proper thing to do in this case is anyhow. As a user
I'd hope I'd not be asked with a random interface to repost when some
background JS runs XHR asynchronous from my interaction. That's obviously not
workable. In this case the user of the agent is more like the application code
and XHR doesn't really have the API to support this sort of event AFAIK.

Regardless, that is a separate issue to what is being discussed, and the
article does mention it; I'm sure it's not that it was forgotten. Standards
documents aren't always perfect in predicting every possible scenario so there
must always be room for practical implementation and I think IE has a very
good argument to make in its favor for this specific case.

~~~
ldar15
Its pretty standard that "MUST NOT" in specs really means "MUST NOT".

 _I think IE has a very good argument to make in its favor for this specific
case_

Great. What is it?

~~~
strmpnk
... and magically breaking 10 years of web applications is also a big MUST
NOT. Why else would the spec itself dedicate an entire paragraph and add two
response codes just for legacy cases? The article, if you read it, also
explains why. All old browsers map POST so they stuck to that for forms and
XHR's broader capabilities map to the standard when possible. That's a pretty
good argument, unless you have better ideas, again, make your suggestions
heard.

------
masklinn
You _must not_ issue redirects to AJAX requests anyway, because Firefox will
reset every single header you set on the request (custom or not), which has
very annoying side effects (content negotiation will be broken post-redirect,
if you're trying to use a RESTful API with content dispatching for browsers
and API clients you're hosed)

~~~
justincormack
What do you mean reset every header? Not set them on the redirect? Is there a
bug outstanding for this?

~~~
masklinn
> What do you mean reset every header? Not set them on the redirect?

When sending a request via XHR you can set request headers. Some are standard
headers (Content-Type, Accept, etc...), others are not but widely used
(X-Requested-With), and others you can make up on the spot. The web server
will get those and can act on that to, for instance, customize what will be
returned, or do cache work, or whatever.

If the XHR request returns a redirection, browsers will follow it
automatically. That's what XHR does. Now most browsers will also re-use the
headers you set (minus those for which it makes no sense maybe, not sure about
that one actually but I'd expect things like ETag or Last-Modified could be
re-set by the browser or stripped off).

Not Firefox. On the other side of the redirection, your new XHR request will
not have _any_ of the headers you set. No standard ones (like Accept) and no
custom ones (X-Requested-With). All of those will disappear and Firefox will
solely send its default sequence of headers. Which definitely are not the ones
you want since you bothered customizing that.

> Is there a bug outstanding for this?

Yep: <https://bugzilla.mozilla.org/show_bug.cgi?id=553888>

it looks like it might be fixed. In Firefox 7.

~~~
nomurrcy
This is 'fixed' however in a bunch of server-side frameworks. (I'm not sure
about rails, I don't use it)

I've used a hack (I think lifted from django) whereby if you are running
firefox and and your request came in with an x-requested-with header and we
redirect you, we keep a marker in your session that we just redirected you to
url x via xhttp request and store any other custom headers we might have set.

Then when your subsequent request comes in (with all its custom headers
including x-requested-with missing) we re-add the headers that were stripped
out and route your request based on the infered headers. We only keep this
lookup entry around for a very short time (like 30 seconds) and remove it
after the first time it is used.

It is a total hack, but it works in most cases. Not using redirects on xmlhttp
requests is pretty limiting - it is a real shame that the FF devs took so long
to fix this bug as it now exits in tons of installs that will probably be
around for years to come.

~~~
masklinn
> It is a total hack, but it works in most cases.

yeah I had to do that kind of stuff as well.

> Not using redirects on xmlhttp requests is pretty limiting

I don't agree. The reality is that it should not be needed, redirect-after-
post (let alone DELETE or PUT) is a workaround for browser UI issues ("Do you
want to re-send this POST request" popups on refresh) and URI localization
(bookmarkability of the landing page). There are reasons for redirects in xhr
scenarios, but they have to do mostly with clients "caching" old URLs and the
service changing its URI structure.

There's little to no reason to send a redirect (let alone a temporary one) to
an XHR.

------
boucher
IE (and pretty much every browser as far as I can tell) has always supported
DELETE in XMLHttpRequests (or MSXML as the case may be). Presumably Rick is
talking about DELETE working in <form>s.

~~~
nbpoole
I just tried it out myself: I can't get DELETE to work in IE9 forms.

My code (I've tried it with both upper-case and lower-case DELETE):

    
    
        <form action="http://example.com" method="DELETE">
          <input type="submit" />
        </form>

~~~
patio11
HTML 4 and HTML 5 both define method as either "get" or "post", mapping to GET
and POST, with no other valid options. This _compliant behavior_ is
widespread, which is why Rails uses Javascript magic to fake the presence of a
method POST or GET parameter in some of the built-in magic. (I only know this
because I've special-cased it for an app I built.)

~~~
elliottcarlson
Though, it should be noted that at one point HTML5 did plan to support PUT and
DELETE, however that was then removed[1] again. Could be part of the confusion
with some people.

I would reference when it was added, but since it's a working draft spec, it's
been removed and I can't find the original addition date - but I believe it to
be mid to late 2009.

[1] [http://www.w3.org/TR/2010/WD-
html5-diff-20101019/#changes-20...](http://www.w3.org/TR/2010/WD-
html5-diff-20101019/#changes-2010-06-24)

~~~
justincormack
It may go back in at some point, it was reopened a couple of months ago...

------
nirvdrum
I don't have the time to investigate right now, but I wonder if returning a
303 rather than a 302 would yield the expected behavior. I.e., a redirect
without cascading effects.

------
guard-of-terra
The problem here is that you can't change the standard, even you know that it
states a silly thing or doesn't state an important one.

You're writing a standard at the time when you know the least about how and
why the thing you spec would be used. You can't get it right (you still can do
"mostly right"), but you desperately need to sneak some fixes.

So yes, you should have hot-fixes to standards just for those cases. One would
state that the response of 302 for POST/PUT/DELETE should unconditionally
trigger GET. And it should be in the spec years ago.

And no, you can't just "increment version number". Realistically you can't,
pretending you can is silly too.

There is the same problem with laws: you first pass them, then you discover
some flaws but no easy way to patch.

------
Sniffnoy
As someone not too familiar with HTTP -- given that you're redirecting them to
a an actual page and not some user-associated resource the page needs to grab,
why would the redirected DELETE request be honored in the first place? Or am I
misunderstanding what's going on here?

~~~
stanleydrew
I think what you are suggesting is that a DELETE request to a resource that
should not (in the application developer's mind) be deleted should be
disallowed by said developer.

While this protection is a good idea, it doesn't change the fact that a
redirect to a resource that _is_ DELETEable would have an unexpected and
unpleasant side-effect if the application developer was expecting the browser
to issue a GET request.

Think about, e.g., a "delete this message and go to the next in your inbox"
kind of functionality being implemented like this. Not that such an
implementation would be the best idea, but it's a thought experiment.

~~~
FuzzyDunlop
Request method specificity in defining API endpoints is common because it
allows the re-use of the same URI for different purposes.

So in some cases you could get a 404, or the endpoint treats everything as a
GET request, or as you say the redirect URI actually has its own delete method
that you assume won't be used after a redirect.

It seems easy to blame the browser, not least because it's also IE, but your
code should take stuff like that into account, _especially_ when dealing with
destructive requests.

To be honest though, and this may be through ignorance on my part, I don't get
why you'd issue a redirect on an AJAX request after performing a delete or
whatever.

If I sent an AJAX request to delete a widget I wouldn't expect to get any data
back, because I'm not asking for it. I'd want at least an empty 200 OK
response for a successful delete or a relevant error code. And then I could
react accordingly based on the result.

The design that allows IE9 to do this appears to just cut corners.

------
dw0rm
It seems that IE behavior is correct, and rails should return 303 redirect.

~~~
technoweenie
It's a vague spec, and IE behaves differently than every other browser out
there. It's vague enough that they added additional redirection codes (303 and
307).

~~~
dw0rm
I see your point. Although, I've been using 303 redirect for this purpose in
my app after reading the specs, and haven't experienced any problems yet. So
maybe you should consider switching to 303.

------
tombot
+1 for planetary references

