如何使用fetch()和原生JavaScript伪造.xhr请求
结论
完全可以实现,没有任何技术障碍。
原理说明
你贴出的是Ruby on Rails框架的标准评论提交处理逻辑,其中request.xhr?的判断规则非常简单,没有任何复杂的客户端指纹校验:它只会检查请求头中是否存在值为XMLHttpRequest的X-Requested-With字段。
jQuery的$.ajax系列方法默认会给所有异步请求自动加上这个请求头,所以原有jQuery代码可以正常拿到评论片段的返回;而原生fetchAPI默认不会携带这个头,服务端会将请求判定为普通非异步请求,走到重定向分支。
实现方案
你只需要在发起fetch请求时,手动在请求头配置里加上X-Requested-With: XMLHttpRequest即可。同域场景下添加这个自定义头不会有任何限制,完全可以被服务端正常识别。
绝大多数Rails站点默认开启CSRF安全校验,你还需要同步携带页面中预置的CSRF令牌,否则请求会被安全策略拦截。
参考实现代码
const commentForm = document.querySelector('#comment-form') commentForm.addEventListener('submit', async (e) => { e.preventDefault() const formData = new FormData(commentForm) const response = await fetch(commentForm.action, { method: commentForm.method.toUpperCase() || 'POST', body: formData, headers: { // 核心配置:添加XHR请求标识 'X-Requested-With': 'XMLHttpRequest', // 携带Rails要求的CSRF令牌 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]')?.content }, // 兜底配置:禁止fetch自动跟随重定向 redirect: 'manual' }) if (response.ok) { // 服务端返回的是评论HTML片段,直接插入页面对应位置即可 const commentHtml = await response.text() document.querySelector('.comment-list').insertAdjacentHTML('beforeend', commentHtml) commentForm.reset() } })
注意事项
- 不要给携带
FormData的fetch请求手动设置Content-Type请求头,浏览器会自动生成带正确boundary边界值的multipart/form-data头,手动设置会导致服务端无法正常解析表单数据 - 该方案完全适配同域前端重构场景,跨域场景下这个自定义头会触发CORS预检请求,需要服务端返回对应许可头才能使用,你没有后端权限的情况下跨域场景无法生效,但重构本站前端不存在这个问题
redirect: 'manual'是兜底配置,就算头配置出错导致服务端返回重定向,fetch也不会自动触发页面跳转,方便调试定位问题
内容的提问来源于stack exchange,提问作者YmeYnot45
相关产品推荐
相关产品推荐

