【问题标题】:Being served a response directly from the cache when clicking back button单击后退按钮时直接从缓存中获得响应
【发布时间】:2012-10-19 06:35:09
【问题描述】:

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

相关代码

我有一个client-side Backbone router,其路由采用名为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 响应。没有请求发送到服务器。

应用日志

您可以在 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

可能相关的问题

It seems like this guy was having a very similar problem to me.

现场样本

如果您真的愿意,可以在 Heroku 上的暂存服务器上实时查看问题。

重现步骤(编辑:自从我修复了问题后,这些不再起作用)。

  1. 使用电子邮件登录here:user@example.com 并通过:foobar
  2. 访问http://print-staging.herokuapp.com/customers/2。您应该会看到一个看起来很乱的对话框打开。
  3. 单击对话框中的“新建作业”小链接。页面应该会发生变化,并且应该会打开一个新对话框。
  4. 点击浏览器后退按钮。

【问题讨论】:

    标签: ruby-on-rails caching backbone.js browser-history pushstate


    【解决方案1】:

    您可以在服务器端的响应中添加一个 no-cache 标头,这应该指示浏览器不缓存响应:

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

    【讨论】:

    • 好的,但为什么我必须这样做?我实际上确实希望浏览器在大多数情况下缓存响应(它可能不会有太大变化)。我只是不希望后退按钮向我显示缓存的响应。我认为基本上我想发送一个 JSON 请求,但是当用户按下后退按钮时,我想发出一个 HTML 请求而不是 JSON 请求。
    • 对不起,我不明白你的意思大卫......你想如何使用缓存但你不希望后退按钮显示缓存的响应?
    • 我想我需要一种方法来获取 JSON GET 请求,以便不在浏览器历史堆栈中输入条目(尽管事实上我将 pushState 与 Backbone 一起使用。那样我可以按后退按钮,它会跳过 JSON 请求并向用户显示之前的请求(希望这是一个 HTML 请求应该能够形成一个实际页面来显示用户)。也许这是不可能的,我不确定?
    • 好的,我明白你的意思了.. 但我注意到点击 New Job 会启动一个新请求(而不是点击 new Employee),它是否甚至与您在 Backbone 中创建的路由匹配 .. 它是由于代码被缩小,我很难调试
    • 因此,基本上,当您单击“新建作业”时,它会对“/jobs/new”执行完整的 HTML 请求。我已经修改了jobs#new 操作,以便它简单地呈现作业控制器的索引模板。渲染该模板后,JS 启动,客户端路由器运行您在原始问题中看到的 newForContact 方法。
    【解决方案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 请求。唯一的问题是,当返回到需要几秒钟才能获得服务器响应的页面时,屏幕需要几秒钟才能改变。并且没有微调器或忙碌进度指示器。

    【讨论】:

      猜你喜欢
      • 2012-02-06
      • 2012-05-07
      • 2013-03-21
      • 1970-01-01
      • 1970-01-01
      • 2023-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多