Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
IE9 deletes stuff (techno-weenie.net)
169 points by srozet on Aug 19, 2011 | hide | past | favorite | 35 comments


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.


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.... 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.


> 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


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.


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 :-)


Please see http://blogs.msdn.com/b/ieinternals/archive/2011/08/19/under... for a complete discussion of behavior.


We didn't see the issue in IE8 when we developed the feature, or this week when we tracked down the IE bug.

Something changed with IE9. Maybe jQuery detected it supports DELETE requests? I have no idea.


So I guess everything is really fine: This behavior mentioned in the last section was likely even more surprising because Internet Explorer does convert POST to GET for HTTP/301 and HTTP/302, like all other browsers


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.


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.


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?


... 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.


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)


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


> 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.


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.


> 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.


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.


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>


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.)


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...


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


DELETE isn't supported by HTML forms. You'd have to submit it through ajax or Mozilla Poster/Chrome XHR Poster.


Modified, thanks.


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.


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.


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?


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.


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.


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


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).


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.


Incorrect. The IE behavior is also wrong, just in a different way: http://www.ietf.org/rfc/rfc1945.txt


According to http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10...

    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.


+1 for planetary references




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: