导读:本期聚焦于甜甜圈创作的《jQuery跨域请求报错?用$.ajaxPrefilter自动添加X-Requested-With头的正确姿势》,敬请观看详情。跨域请求被后端拦截,提示不是AJAX请求?问题很可能出在X-Requested-With请求头上。当请求跨域时,浏览器会先发送OPTIONS预检请求,如果直接添加自定义头而没有在服务端放行,请求就会失败。本文详细介绍$.ajaxPrefilter的作用机制,讲解它如何拦截jQuery发出的每一个AJAX请求,并借助正则匹配判断请求是否跨域,再决定是否注入X-Requested-With头。文中还会对比手动在每个请求里写headers的弊端,分析预检请求触发的原因,以及服务端CORS配置中Access-Control-Allow-Headers的正确写法,帮你彻底解决跨域环境下前后端识别AJAX请求的难题。

在使用jQuery做前后端分离项目时,后端经常需要通过X-Requested-With这个请求头来判断当前请求是否为AJAX请求。同域环境下,jQuery会自动带上这个头,一切正常。可一旦请求变成跨域,情况就复杂了:直接添加这个自定义头会触发浏览器的预检请求(OPTIONS),如果服务端CORS配置没有放行该头,请求直接失败;而不添加的话,后端又无法正确识别AJAX请求。本文就来聊聊如何利用$.ajaxPrefilter这个全局拦截器,优雅地处理这个问题。

jQuery跨域请求报错?用$.ajaxPrefilter自动添加X-Requested-With头的正确姿势

一、先弄清楚X-Requested-With在跨域场景下的问题根源

jQuery在发送AJAX请求时,默认会在请求头中带上X-Requested-With: XMLHttpRequest,很多后端框架(比如Java的Spring、PHP的各种框架)都依赖这个头来区分AJAX请求和普通页面请求。在同域环境下,这个头属于“简单头”的一部分吗?其实不是。X-Requested-With并不在浏览器允许的“简单请求头”列表中,同域时不受CORS约束所以没感觉,一旦跨域,添加它就会让请求从“简单请求”变成“预检请求”。

所谓预检请求,就是浏览器在正式发送请求之前,先向服务端发一个OPTIONS方法的请求,询问服务端是否允许该跨域请求携带这些自定义头。如果服务端返回的Access-Control-Allow-Headers中没有包含X-Requested-With,浏览器就会直接拒绝正式请求,控制台会报类似“Request header field X-Requested-With is not allowed by Access-Control-Allow-Headers”的错误。

所以问题的解法有两个方向:一是服务端在CORS响应头中明确放行X-Requested-With,二是前端只在必要的时候添加这个头。而前端这边,如果每个请求都手写一遍headers配置,代码会非常冗余,这时候$.ajaxPrefilter就派上用场了。

二、$.ajaxPrefilter的运作机制与基本用法

$.ajaxPrefilter是jQuery提供的全局AJAX请求预过滤器,它会在每个$.ajax(包括$.get$.post$.getJSON这些封装方法)真正发送之前被调用。你可以把它理解成一个全局的请求拦截器:所有请求的配置对象(options)在发出去之前,都会先经过你注册的处理函数,你有机会在这里修改url、headers、type等任何配置。

它的基本签名是$.ajaxPrefilter(fn),其中fn接收三个参数:options是当前请求的配置对象,originalOptions是未经jQuery处理的原始配置,jqXHR是请求的XMLHttpRequest对象的jQuery包装。此外,你还可以给prefilter指定一个dataTypes过滤条件,比如$.ajaxPrefilter('json script', fn)表示只拦截dataType为json或script的请求。

先看一个最简单的全局添加请求头的写法:

// 全局拦截:为所有AJAX请求添加X-Requested-With头
$.ajaxPrefilter(function(options) {
    // 初始化headers对象,避免覆盖用户已配置的头
    options.headers = options.headers || {};
    options.headers['X-Requested-With'] = 'XMLHttpRequest';
});

这段代码对所有请求一视同仁,简单粗暴。但如果你的页面中同时存在同域和跨域请求,同域请求jQuery本来就自动带这个头,重复设置倒也无害。真正需要考虑的是:跨域请求经过这个拦截器添加头之后,是否会导致原本可以“简单请求”直接通过的请求,变成触发预检的请求?答案是肯定的。所以更精细的做法,是判断请求目标是否跨域再决定是否添加。

三、实战:判断跨域后再自动注入请求头

判断一个请求URL是否跨域,可以借助正则表达式提取域名部分,与当前页面的location.host做比较。如果域名不同,就是跨域请求;此时我们再决定是否添加X-Requested-With。这种方案的好处是,同域请求走jQuery默认行为,跨域请求则统一由拦截器处理,业务代码完全不用关心这些细节。

// 提取URL中协议、域名、端口部分的正则
var rurl = /^([\w.+-]+:)?(?:\/\/([^\/?#]*)|[^\/?#]|#)/i;

$.ajaxPrefilter(function(options) {
    // 如果当前请求没有显式配置crossDomain,则根据URL自动判断
    if (options.crossDomain === undefined) {
        var parts = rurl.exec(options.url);
        options.crossDomain = !!(parts &&
            (parts[1] !== location.protocol || parts[2] !== location.host));
    }

    // 跨域请求时才添加X-Requested-With头
    if (options.crossDomain) {
        options.headers = options.headers || {};
        options.headers['X-Requested-With'] = 'XMLHttpRequest';
    }
});

这段代码借鉴了jQuery源码中判断跨域的思路。先尝试从URL中解析出协议和主机部分,如果解析结果与当前页面的location.protocollocation.host不一致,就标记为跨域。注意location.host包含端口号,而location.hostname不包含,判断时要保持口径一致,否则8080这类非默认端口的场景会判断出错。

还有一种更省事的方式:直接利用jQuery自己维护的options.crossDomain属性。jQuery在内部处理请求时,如果发现请求目标是跨域的,会把这个标志设置为true,同时jQuery在跨域时默认不会发送X-Requested-With头(正是为了避免触发预检)。所以如果你确定服务端已经放行了该头,只需在prefilter中根据这个标志补回头部即可:

$.ajaxPrefilter(function(options) {
    // 跨域时jQuery会默认剥离X-Requested-With,这里手动补回
    if (options.crossDomain) {
        options.headers = options.headers || {};
        options.headers['X-Requested-With'] = 'XMLHttpRequest';
    }
});

四、服务端CORS配置必须配套放行

前端做了拦截器处理后,别忘了服务端也要配合。如果服务端不放行X-Requested-With,前端做再多工作也是白搭,预检请求依然会失败。以Nginx为例,需要在跨域响应中加上这样的配置:

add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, X-Requested-With";
add_header Access-Control-Allow-Credentials true;

# 处理预检请求:OPTIONS请求直接返回204
if ($request_method = OPTIONS) {
    return 204;
}

关键点有两个:一是Access-Control-Allow-Headers中必须明确包含X-Requested-With,写法不区分大小写;二是预检的OPTIONS请求必须正确响应,有些框架或服务器会把OPTIONS请求当成业务请求处理,返回405或404,导致预检失败,所以最好在Nginx或网关层把OPTIONS请求直接短路返回204。

另外要注意,如果前端请求带了cookie(withCredentials为true),服务端的Access-Control-Allow-Origin不能写成通配符*,必须回显具体的来源域名,否则浏览器同样会拒绝。这是跨域配置中最常见的坑之一。

五、几点补充建议

首先是执行顺序问题。$.ajaxPrefilter注册的处理函数遵循先进先出的顺序依次执行,如果你在多个地方注册了prefilter,注意它们对options的修改会叠加。其次是兼容性,如果你的项目还在用JSONP处理跨域,要注意JSONP本质上是script标签加载,根本没有请求头的概念,给JSONP请求设置headers是无效的,prefilter里可以通过options.dataTypes判断并跳过。

最后一点建议:这种全局拦截逻辑最好封装在一个独立的js文件中,在所有业务脚本之前引入,保证拦截器在任何请求发出之前注册完毕。同时配合$.ajaxSetup可以统一设置timeout、contentType等默认值,形成一套完整的全局请求规范。这样一来,业务代码只需要关心接口本身,跨域、请求头这些脏活全部交给统一层处理,代码可维护性会明显提升。

jQueryajaxPrefilter跨域请求修改时间:2026-09-13 20:53:00

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260913/56238.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。