点击后退按钮时,从缓存中直接获取响应。

10

请注意,我认为这个问题与Backbone或JavaScript无关,但需要包含一些Backbone代码作为问题的上下文。

相关代码

我有一个客户端Backbone路由器,其中一个路由接受名为contactId的参数。它看起来类似于这样:

Backbone.Router.extend({

  routes: {
    "jobs/new?contact_id=:contactId": "newForContact"
  },

  // Fetch the contact and initialize a new job model which 
  // is associated with that contact.
  newForContact: function(contactId) {
    var contact = new Contact(id: contactId);
    contact.fetch({
      success: _.bind(function(model, resp) {
        var job = new Job(contact: contact);
        this.new(job);
      }
    }, this));
  },

  // Show the JobView for the given job.
  new: function(jobModel) {
    view = new JobView(job: jobModel);
    $('body').append(view.render().el);
  }
};

现在我正试图使用已开启 pushState 的这个设置。

当我访问触发 newForContact 路由的页面时,一切都按预期工作。但是,如果此时我点击浏览器的后退按钮,浏览器会直接从缓存中提供来自 contact.fetch() 方法的JSON响应,而不向服务器发送任何请求。

获取JSON响应

应用程序日志

您可以在Rails应用程序日志中查看此情况。在此部分中,我访问触发 newForContact 的路由。

Started GET "/jobs/new?contact%5Bid%5D=1&contact%5Btype%5D=Customer" for 127.0.0.1 at 2012-10-31 22:41:48 +0000
Processing by JobsController#new as HTML
  Parameters: {"contact"=>{"id"=>"1", "type"=>"Customer"}}
  User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 1]]
  Business Load (0.3ms)  SELECT "businesses".* FROM "businesses" WHERE "businesses"."id" IN (1)
  Rendered shared/_search_form.html.erb (0.3ms)
  Job Load (0.4ms)  SELECT "jobs".* FROM "jobs" WHERE "jobs"."business_id" = 1 ORDER BY created_at desc
  Rendered jobs/_list.html.erb (1.5ms)
  Rendered jobs/index.html.erb within layouts/application (4.1ms)
  Rendered layouts/_head_content.html.erb (0.7ms)
  Rendered layouts/_flash.html.erb (0.0ms)
Cache read: views/jobs/main_nav/d6a805d9b6f285e424f207add4f35595
Read fragment views/jobs/main_nav/d6a805d9b6f285e424f207add4f35595 (0.4ms)
  Rendered layouts/_nav.html.erb (0.6ms)
  Rendered layouts/_header.html.erb (0.7ms)
Completed 200 OK in 12ms (Views: 8.0ms | ActiveRecord: 1.0ms)
Cache read: http://print.dev/customers/1?

你可以看到它在这一点上使用JSON请求来获取联系人。

Started GET "/customers/1" for 127.0.0.1 at 2012-10-31 22:41:48 +0000
Processing by CustomersController#show as JSON
  Parameters: {"id"=>"1"}
  User Load (0.5ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 1]]
  Business Load (0.4ms)  SELECT "businesses".* FROM "businesses" WHERE "businesses"."id" IN (1)
  Customer Load (0.2ms)  SELECT "customers".* FROM "customers" WHERE "customers"."business_id" = 1 AND "customers"."id" = $1 LIMIT 1  [["id", "1"]]
  CustomerEmployee Load (0.3ms)  SELECT "customer_employees".* FROM "customer_employees" WHERE "customer_employees"."employer_id" IN (1)
  Job Load (0.4ms)  SELECT "jobs".* FROM "jobs" WHERE "jobs"."contact_type" = 'Customer' AND "jobs"."contact_id" IN (1)
  Invoice Load (0.3ms)  SELECT "invoices".* FROM "invoices" WHERE "invoices"."client_id" IN (1)
  Job Load (0.5ms)  SELECT "jobs".* FROM "jobs" WHERE "jobs"."contact_id" = 1 AND "jobs"."contact_type" = 'Customer' AND "jobs"."state" = 'finished' AND "jobs"."invoice_id" IS NULL
  Rendered customers/show.json.rabl (2.8ms)
Completed 200 OK in 67ms (Views: 3.2ms | ActiveRecord: 2.5ms)

此时,我会点击浏览器的返回按钮,但服务器上没有记录新的请求。

Rails 环境

这只发生在我的演示服务器(Heroku)上,而不是在开发环境中。我可以通过在启用缓存的 Rails 配置的演示环境中使用 Pow 运行应用程序来在本地重新创建它。

config.action_controller.perform_caching = true

请注意,即使启用了缓存,我也无法在开发环境中重现此错误。

此问题出现在 Chrome 22.0.1229.94FF 16.0.2Safari 6.0.1 中。我正在使用 Rails 3.2.8

可能相关的问题

看起来像是这个人遇到了和我非常相似的问题。

在线演示

如果您真的想查看问题,请在 Heroku 上访问我的暂存服务器。

复现步骤(编辑:由于我修补了这个问题,所以这些步骤不再适用)。

  1. 使用电子邮件 user@example.com 和密码 foobar 在此处登录
  2. 访问http://print-staging.herokuapp.com/customers/2。您应该会看到一个破坏的对话框弹出。
  3. 单击对话框中的小“New job”链接。页面应该会更改并打开一个新的对话框。
  4. 单击浏览器的后退按钮。
2个回答

10

你可以在服务器端向响应头添加no-cache头,这将指示浏览器不要缓存响应:

response.headers["Cache-Control"] = "no-cache"

1
好的,但是为什么我要这样做呢?大多数情况下,我确实希望浏览器缓存响应(它可能不会经常更改)。我只是不想让后退按钮显示缓存的响应。我认为基本上我想发送一个JSON请求,但当用户按下后退按钮时,我想发出HTML请求而不是JSON请求。 - David Tuite
抱歉,David,我不明白你的意思... 你想如何使用缓存,但是又不想让后退按钮显示已缓存的响应? - kabaros
1
我想我需要一种方法来使JSON GET请求不在浏览器历史记录中创建条目(尽管我正在使用Backbone的pushState)。这样,我可以按下返回按钮,它会跳过JSON请求并向用户显示该请求之前的内容(这将是一个HTML请求,应该能够形成一个实际的页面以向用户展示)。也许这是不可能的,我不确定? - David Tuite
好的,我明白你的意思。但是我注意到点击“新工作”会启动一个新的请求(而不是点击“新员工”),它是否匹配了你在Backbone中创建的路由呢?由于代码被压缩了,所以在我的一面进行调试很困难。 - kabaros
你知道吗,我对你的解决方案进行了一些玩耍...它实际上是有效的。我稍微扩展了一下看起来像这样,它确实解决了问题。我认为,如果我可以修改它,使其仅防止浏览器缓存JSON请求,那么它就足够好了。 - David Tuite
显示剩余3条评论

2

我没有使用Backbone,这只是我使用AJAX / pushstate完成的


我在我的AJAX响应中使用了history.pushState()

var url = "http://foo.com/path/to/page";
var relative_url = UrlToRelative(url); // some function to convert a URL to relative
var absolute_url = UrlToAbsolute(url); // some function to convert a URL to absolute
history.pushState(
  {
    hash: "#"+relative_url,
    title: document.title,
    initialHref: absolute_url
  },
  document.title,
  url
);

我有一个onPopState的事件监听器:

window.onpopstate = function(event) {
  if (event.state && event.state.initialHref) {
    $.ajax({
      complete: null,
      data: {},
      dataType: "script",
      success: null,
      type: 'GET',
      url: event.state.initialHref
    });
  }
}

这个事件监听器会发起一个AJAX请求。唯一的问题是,当返回到需要几秒钟才能从服务器获得响应的页面时,屏幕需要几秒钟才能改变。而且没有旋转器或忙碌进度指示器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接