Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Back Button (Chrome) Gets Json instead of HTML in Play Framework

Folks, I've got a web application where I have re-used the same route for the JSON and HTML representations of the same resource, let's call it /foo/details for now. This page is linked from, let's call it /bar/details. (so, looking at /bar/details you see link to -> /foo/details).

When I go from the first page to the second, everything works fine. When I click the back button in Chrome, the original page renders as JSON instead of HTML. If I hit refresh in the browser, I get the HTML representation and not the JSON.

Here's the code I'm using to detect JSON vs HTML:

res.result.map { group =>
  render {
    case Accepts.Html() => Ok(views.html.groups.details(group))
    case Accepts.Json() => Ok(Json.toJson(group))
  }
}.getOrElse(NotFound)

This is the standard implementation of this pattern and it works everywhere, except when I use the back button in Chrome in certain situations.

Is there some value I'm not clearing, or something my pages are doing with Ajax that is confusing Play to make it render in Json, or perhaps Chrome is caching the page but caching the wrong accepts header??

I can get around this by using two different routes, one for Json and one for Html, but I dislike that as it feels like I'm giving up.

Anybody have any ideas as to what causes this behavior in the back button only?

like image 430
Kevin Hoffman Avatar asked May 06 '13 14:05

Kevin Hoffman


People also ask

How do I restore the Back button in Chrome?

Google Chrome Open the Chrome menu and select Settings. Then, select Advanced > Reset and clean up > Restore settings to their original defaults.

What happens when browser Back button is pressed?

For pages that are set as non-cached, the browser reloads the page from the server when you press Back, as though it was the first time you are visiting it. For cached pages, the browser displays it out of the cache, which is much faster.


2 Answers

Kevin you are right. However, there is another solution.

If you add "Vary: Accept" to the response header, it will make chrome and other browsers with this issue (eg Firefox v 21) differentiate between json and html cache. NOTE: Vary: Accept-Encoding header does not work, as far as I have tested.

If you use nginx, you can set this: http://wiki.nginx.org/HttpProxyModule#proxy_set_header

proxy_set_header Vary Accept;

However, there is an issue with nginx where the vary accept header will not be sent sometimes, something to do with the cache. See http://wiki.nginx.org/HttpProxyModule#proxy_set_header To deal with this, you can turn gzip_vary on for nginx, but this will send Vary: Accept-Encoding header. This header does not solve the issue.

I use rails, and I used a before_filter where i modified response.headers["Vary"]= "Accept"

I'm sure there are other ways of doing this with other servers / frameworks.

More info: https://web.archive.org/web/20130114082345/http://blog.sdqali.in/blog/2012/11/27/on-rest-content-type-google-chrome-and-caching/

like image 147
jay Avatar answered Oct 04 '22 06:10

jay


It was definitely Chrome's browser cache. It makes no distinction between a request made to /foo/bar with "Accept"->"application/json" and with the regular HTML accept header. As a result, I would load the HTML page, the JavaScript on that page would hit the same URL for raw JSON data, and then the next time I hit back, Chrome would serve up the cached JSON instead of the cached HTML.

As a result, I had to modify my routes so that my JSON/REST API all go through different URLs so that Chrome (and Safari) don't cache the JSON.

like image 23
Kevin Hoffman Avatar answered Oct 04 '22 06:10

Kevin Hoffman