Rails 6.1.7中Turbolinks与CSP Nonce兼容渲染局部视图问题
问题分析与解决方案
1. turbolinks:request-start事件未触发的原因
turbolinks:request-start仅在Turbolinks主导的页面导航请求(比如点击Turbolinks处理的链接)时触发,你用jQuery $.ajax发起的自定义Ajax请求不会触发该事件,因此application.js中的console.log永远不会执行。
2. 修复方案:自定义Ajax请求的Nonce传递与CSP兼容
方案一:简化版,直接处理Ajax请求与Nonce
既然你已经在Ajax代码中手动设置了HTTP_X_TURBOLINKS_NONCE请求头,无需依赖Turbolinks事件,按以下步骤调整:
步骤1:更新CSP配置,添加Nonce指令
当前script_src未包含nonce规则,需要修改CSP配置,确保内联脚本(包括返回的js.erb代码)能被允许:
policy.script_src :self, :https, "https://code.jquery.com/", "https://www.google-analytics.com", -> request { "'nonce-#{request.content_security_policy_nonce}'" }
步骤2:用Nonce包裹返回的JS代码
Rails渲染js.erb时不会自动添加nonce,需手动用javascript_tag包裹内容:
<%= javascript_tag(nonce: true) do %> $('#my_frame').html("<%= j (render partial: 'frame_content', locals:{label: @label}) %>") <% end %>
步骤3:移除无效的事件监听
删除application.js中针对turbolinks:request-start的监听代码:
// 删掉这段无效代码 window.onload = function () { document.addEventListener("turbolinks:request-start", function (event) { console.log("in coming request"); var xhr = event.data.xhr; xhr.setRequestHeader( "X-Turbolinks-Nonce", $("meta[name='csp-nonce']").prop("content") ); }); };
方案二:改用Turbolinks发起请求,贴合生态
如果想利用Turbolinks的事件机制,可使用Turbolinks的fetch方法发起请求,此时turbolinks:request-start事件会正常触发:
<%= javascript_tag(nonce: true) do %> Turbolinks.fetch('/get_matching_labels', { method: 'POST', // 匹配你的路由HTTP方法 headers: { 'X-Turbolinks-Nonce': '<%= content_security_policy_nonce %>', 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ label: 'test' }) }).then(response => response.text()) .then(data => { eval(data); console.log('success'); }) .catch(() => console.log('failure')); <% end %>
这种情况下,你之前在application.js中的事件监听代码会生效,因为请求由Turbolinks发起。
3. 关键注意事项
- Nonce是每个请求唯一的,但通过请求头传递后,Rails的nonce生成器会复用该值,确保返回脚本的nonce与当前页面一致
- 所有内联脚本(包括Ajax返回的内容)必须用
javascript_tag(nonce: true)包裹,或在CSP的script_src中包含正确的nonce规则 - 排查CSP问题时,优先查看浏览器控制台的报错信息,这是最直接的问题定位依据
内容的提问来源于stack exchange,提问作者Maryline Rs
相关产品推荐
相关产品推荐

