6

Here's the situation:

I have a web application which response to a request for a list of resources, lets say:

/items

This is initially requested directly by the web browser by navigating to that path. The browser uses it's standard "Accept" header which includes "text/html" and my application notices this and returns the HTML content for the item list.

Within the returned HTML is some JavaScript (jQuery), which then does an ajax request to retrieve the actual data:

/items

Only this time, the "Accept" header is explicitly set to "application/json". Again, my application notices this and JSON is correctly returned to the request, the data is inserted into the page, and everything is happy.

Here comes the problem: The user navigates to another page, and later presses the BACK button. They are then prompted to save a file. This turns out to be the JSON data of the item list.

So far I've confirmed this to happen in both Google Chrome and Firefox 3.5.

There's two possible types of answers here:

  1. How can I fix the problem. Is there some magic combination of Cache-Control headers, or other voodoo which cause the browser to do the right thing here?

  2. If you think I am doing something horribly wrong here, how should I go about this? I'm seeking correctness, but also trying not to sacrifice flexibility.

If it helps, the application is a JAX-RS web application, using Restlet 2.0m4. I can provide sample request/response headers if it's helpful but I believe the issue is completely reproducible.

Mark Renouf
  • 30,697
  • 19
  • 94
  • 123
  • This looks like a future problem I'm going to have after I figure out (http://stackoverflow.com/questions/5250923). I'm curious, did you end up sticking with this solution or ultimately did you abandon it for different URLs? The cleanliness of the single RESTful URL for different representations of the same resource is certainly ideal. – mckamey Mar 09 '11 at 19:25

3 Answers3

6

Is there some magic combination of Cache-Control headers, or other voodoo which cause the browser to do the right thing here?

If you serve different responses to different Accept: headers, you must include the header:

Vary: Accept

in your response. The Vary header should also contain any other request headers that influence the response, so for example if you do gzip/deflate compression you'd have to include Accept-Encoding.

IE, unfortunately handles many values of Vary poorly, breaking cacheing completely, which might or might not matter to you.

If you think I am doing something horribly wrong here, how should I go about this?

I don't think the idea of serving different content for different types at the same URL is horribly wrong, but you are letting yourself in for more compatibility problems than you really need. Relying on headers working through JSON isn't really a great idea in practice; you'd be best off just having a different URL, such as /items/json or /items?format=json.

bobince
  • 528,062
  • 107
  • 651
  • 834
  • Serving a different resource at the same URL is wrong; serving different representations of the same resource is what HTTP is supposed to do. Do you want to see it in XML, JSON, nicely-formatted readable HTML or plaintext? It's always the same thing, but with your choice of formats. Unfortunately some browsers break this and you have to use a hack like in the response. If your service offers both "application/json" and "text/html", Internet Explorer will fetch the JSON version because of its screwy Accept header. – Mark Lutton Oct 29 '09 at 15:25
1

I know this question is old, but just in case anyone else runs into this:

I was having this same problem with a Rails application using jQuery, and I fixed it by telling the browser not to cache the JSON response with the solution given here to a different question:

jQuery $.getJSON works only once for each control. Doesn't reach the server again

The problem only seemed to occur with Chrome and Firefox. Safari was handling the back behavior okay without explicitly having to tell it to not cache.

Community
  • 1
  • 1
bobfet1
  • 1,603
  • 21
  • 22
  • This answer worked for me, if you set the "cache" option in your jQuery.ajax() request to "false" you can hit back and it will work as expected – Scott Harvey Sep 08 '11 at 05:33
0

Old question, but for anyone else seeing this, there is nothing wrong with the questioner's usage of the Accept header.

This is a confirmed bug in Chrome. (Previously also in Firefox but since fixed.)

http://code.google.com/p/chromium/issues/detail?id=94369

Tom Christie
  • 33,394
  • 7
  • 101
  • 86