【问题标题】:Raw javascript rendered on back button click在后退按钮单击时呈现的原始 javascript
【发布时间】:2012-09-25 16:40:53
【问题描述】:

我们的团队最近遇到了一个最初的边缘情况问题,当用户从具有某种 javascript 呈现(无论是 Ajax、选项卡等)的页面单击浏览器的后退按钮时,会显示原始 javascript。要重新创建,我们遵循以下步骤:

  • 访问用户职位申请索引
  • 点击一个按钮进入管理职位发布页面
  • 点击一个标签(使用 bettertabs gem)
  • 点击浏览器的返回按钮

前面的步骤会显示:

(function() {

  $(".job_applications").html("<li class=\"job_posting_application\">\n
    ...
    ...
    ...
    ...
  );

}).call(this);

在某些偶然发生的情况下,您无需在返回上一页之前单击选项卡,但它仍会呈现原始 javascript。归根结底,似乎最后渲染的模板正在被缓存,这在浏览器方面是正常的和预期的,但会导致我认为是一个更大的问题。

Layouts and Rendering 部分中的 Rails 指南状态,特别是关于模板的 MIME 类型:

默认情况下,Rails 将使用 text/html 的 MIME 内容类型(或 application/json 如果您使用 :json 选项或 application/xml 用于 :xml 选项)提供渲染操作的结果.).

根据 Rails 的默认设置,我们的控制器的 index 操作应该会渲染我们的 index.html.slim 模板。但是,在跟踪服务器日志的同时对该页面进行非远程调用(例如,直接导航到浏览器中的页面)时,我们注意到它实际上呈现了index.js.coffee。下面是我们的控制器操作,请注意我们没有明确响应 html 或 js 格式,因为我们可能应该考虑此页面中的覆盖功能:

def index
  @company_id, @division_id, @job_posting_id = params[:company_id], params[:division_id], params[:job_posting_id]

  # API requests are made here to instantiate @job_posting, et al.,
  # but are not shown for brevity

  authorize! :manage, @job_posting

  @survey = @job_posting.survey
  @job_applications = @job_posting.job_applications(sort_column, sort_direction)
end

然而,鉴于此设置,index.html.slim 应该基于 Rails 默认值呈现。添加respond_to 块时,缓存似乎仍然有效,控制器可能不太关心respond_to 块的存在:

def index
  ...
  ...
  respond_to do |format|
    format.html
    format.js
  end
end

即使明确告诉每种格式呈现不同的模板,虽然很臭,但似乎js.coffee 模板优先于html.slim 模板:

def index
  ...
  ...
  respond_to do |format|
    format.html { render template: "users/job_posting_applications/index" }
    format.js { render template: "users/job_posting_applications/ajax" }
  end
end

在上述情况下,直接导航到浏览器中的页面(换句话说,不进行远程 Ajax 调用),服务器日志将呈现 ajax.js.coffee,即使 Rails 默认为 html,除非另有说明。

说了这么多,还有一些其他的发现:

Started GET "/users/companies/1/divisions/18/job_postings/349421/applications" for 127.0.0.1 at 2012-10-03 19:55:26 -0400
Processing by Users::JobPostingApplicationsController#index as JSON

您可以参考上面显示的整个请求in this pastie

考虑到我们没有为这个请求提供任何 JSON,并且没有为这个路由的默认格式 :json 提供路由规范,我无法理解为什么它会被处理为 JSON。

此外,在此操作中调试request.format 的值时,它会返回application/json

另一个场景是在另一个控制器 (users/company_admin_metrics#index) 中,它只包含一个 index.html.slim 模板。导航到此页面时,服务器日志显示它在layouts/users 内呈现users/company_admin_metrics/index.html.slim。当我创建一个空白的 js.coffee 模板时:

$ touch app/views/users/company_admin_metrics/index.js.coffee

并直接导航到该索引页面,服务器日志显示它呈现了users/company_admin_metrics/index.js.coffee,这进一步暴露了某个潜在的关于模板呈现优先级的问题。

有没有人遇到过类似的问题可以解决这个问题?

我们的堆栈

下面是这个特定问题的基本参与者的分钟列表:

  • Rails 3.2.6
  • Coffee-Rails 3.2.1
  • Bettertabs 1.2.6

此请求取决于通过解析 JSON 并返回 Ruby 对象的客户端 gem 对我们的职位发布 API 的请求,但这些请求与此特定应用程序的耦合方式不会冲突并导致 this em> 应用程序的内容类型为application/json,用于上述此类请求。

【问题讨论】:

  • 非常深入,但读完所有这些我的眼睛都呆滞了。您需要重新定义问题,但要简明扼要地说明您的问题。忘记与问题没有直接关系的任何事情。
  • 在陈述你的问题时,请准确描述你做了什么,你预期会发生什么,以及实际发生了什么。这包括准确显示 AJAX 请求的外观以及“直接导航”的外观。您可能需要在 AJAX 调用中更明确地说明请求的 mimetype。

标签: jquery ruby-on-rails ajax


【解决方案1】:

我认为您需要将格式扩展 .js 附加到您的 ajax 请求中的 URL。

可能发生的情况是用户在/job_applications 处使用GET 请求html 来点击index 操作。然后 ajax 访问相同的 URL/动作,但请求 javascript。当用户单击后退按钮时,浏览器无法分辨两者之间的区别,因此使用最新的(javascript 响应)。

【讨论】:

    【解决方案2】:

    最近开始在带有 Rails 4.2.4 的 Chrome 中看到这个确切的问题。似乎是一个众所周知且备受争议的问题。我们的团队通过将Vary 标头添加到xhr 请求来解决它:

    class ApplicationController < ActionController::Base
      after_action :set_vary_header
    
      private
    
      # Fix a bug/issue/by-design(?) of browsers that have a hard time understanding
      # what to do about our ajax search page. This header tells browsers to not cache
      # the current contents of the page because previously when someone filtered results,
      # went to the listing's external site, then hit Back, they'd only see the last
      # ajax response snippet, not the full listings page for their search.
      #
      # Heated multi-year discussion on this issue in Chrome
      # https://code.google.com/p/chromium/issues/detail?id=94369
      #
      # And continued by the Rails team:
      # https://github.com/rails/jquery-ujs/issues/318
      # https://github.com/rails/jquery-rails/issues/121
      def set_vary_header
        if request.xhr?
          response.headers["Vary"] = "accept"
        end
      end
    end
    

    【讨论】:

    • 对这个问题的提炼非常有帮助。已经与它搏斗了好几个小时。
    【解决方案3】:

    这个问题被问到已经几个月了,但自从我遇到这个问题以来,我会插话并给我两分钱。

    正如 carbo 所说,浏览器可以分辨 html 和 js 请求之间的区别,因此当单击后退按钮时,它会看到相同的 url,并且只提供发生在 js 代码中的缓存数据。我遇到了一些问题,但是使用推送状态 javascript 来修改 ajax 请求的浏览器历史记录。我找到的最简单的解决方案是告诉浏览器不要缓存请求。因此,当返回上一页时,它会强制重新加载它。

    您可以全局禁用缓存: $.ajaxSetup({ cache: false });

    或者您可以尝试在特定的 ajax 请求上指定缓存 false。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-29
      • 2012-03-22
      相关资源
      最近更新 更多