前端请求拦截模板:axios、XHR、axios + XHR 三种模式

在前端调试、联调、逆向分析、灰度验证时,经常会遇到这样的需求:

  • 某个接口请求发出去前,想临时改 URL 参数
  • 某个请求想强行追加 Authorization
  • 某个 POST 请求想修改请求体里的字段
  • 不改源码,只想在浏览器里临时“劫持”某条请求

这篇文章整理 3 套可直接复用的模板:

  1. axios 拦截
  2. XHR 拦截
  3. axios + XHR 双保险拦截

适用场景:

  • Chrome 控制台临时注入
  • 本地调试
  • 前端页面请求改写
  • 后续博客或项目里直接套模板


先搞清楚:你到底要改什么

请求拦截一般分 3 类:

改 URL 查询参数

例如原请求:

1
http://localhost:1024/prod-api/system/user/getUserList

改成:
1
http://localhost:1024/prod-api/system/user/getUserList?deptId=10001

  • 如果原 URL 没有参数,用 ?
  • 如果原 URL 已经有参数,再追加时用 &

正确示例:

1
2
3
...getUserList?deptId=10001

...getUserList?deptId=10001&status=1


改请求头 Header

例如追加:

1
Authorization: xxxxxxxxx

常用于:

  • 登录态伪造
  • 切换 token
  • 验证接口权限


改请求体 Body

常见于 POST/PUT 请求,比如原 body:

1
2
3
4
{
"pageNum": 1,
"pageSize": 10
}

改成:
1
2
3
4
5
{
"pageNum": 1,
"pageSize": 10,
"deptId": 10001
}


使用前先判断请求类型

在 Chrome 打开开发者工具:

  • F12
  • 切到 Network
  • 点开目标请求

重点看这几个字段:

  • Request Method
  • Request URL
  • Query String Parameters
  • Request Payload
  • Initiator

判断规则:

如果项目是 axios

一般可在源码中直接用 axios.interceptors.request.use(...)

如果页面里是 XMLHttpRequest

一般控制台直接重写 XMLHttpRequest.prototype.open/send

如果你不确定 axios 底层会不会绕过你的逻辑

那就上 axios + XHR 双拦截


axios 拦截模板

适用于:

  • 项目源码中直接接入
  • Vue / React / 后台管理系统
  • 已明确页面请求由 axios 发起

模板 1:axios 拦截 GET 请求,自动追加 URL 参数

1
2
3
4
5
6
7
8
9
10
11
import axios from 'axios'

axios.interceptors.request.use(config => {
if (config.url && config.url.includes('/prod-api/system/user/getUserList')) {
config.params = {
...(config.params || {}),
deptId: 10001
}
}
return config
})

这段代码会把:

1
/prod-api/system/user/getUserList

自动改成:
1
/prod-api/system/user/getUserList?deptId=10001


模板 2:axios 拦截并追加 Authorization

1
2
3
4
5
6
7
8
9
10
11
import axios from 'axios'

axios.interceptors.request.use(config => {
if (config.url && config.url.includes('/prod-api/system/user/getUserList')) {
config.headers = {
...(config.headers || {}),
Authorization: 'Bearer 你的token'
}
}
return config
})

模板 3:axios 同时改 params + headers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import axios from 'axios'

axios.interceptors.request.use(config => {
if (config.url && config.url.includes('/prod-api/system/user/getUserList')) {
config.params = {
...(config.params || {}),
deptId: 10001
}

config.headers = {
...(config.headers || {}),
Authorization: 'Bearer 你的token'
}
}

return config
})

模板 4:axios 改 POST 请求 body

如果请求是 JSON body:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import axios from 'axios'

axios.interceptors.request.use(config => {
if (config.url && config.url.includes('/prod-api/system/user/getUserList')) {
if (config.data && typeof config.data === 'string') {
try {
const data = JSON.parse(config.data)
data.deptId = 10001
config.data = JSON.stringify(data)
} catch (e) {
console.log('data 不是 JSON 字符串,未处理')
}
} else if (config.data && typeof config.data === 'object') {
config.data = {
...config.data,
deptId: 10001
}
}
}
return config
})


XHR 拦截模板

适用于:

  • 在 Chrome 控制台临时注入
  • 老项目
  • axios 底层走 XHR
  • 不改源码,只想临时调试

模板 1:XHR 拦截 GET 请求并追加 URL 参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(() => {
const TARGET = '/prod-api/system/user/getUserList'
const DEPT_ID = '10001'

const oldOpen = XMLHttpRequest.prototype.open
XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
if (typeof url === 'string' && url.includes(TARGET)) {
const u = new URL(url, location.origin)
u.searchParams.set('deptId', DEPT_ID)
url = u.toString()
console.log('[XHR已改写URL]', url)
}
return oldOpen.call(this, method, url, async, user, password)
}
console.log('XHR URL 拦截器已生效')
})()

模板 2:XHR 拦截并追加 Authorization

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(() => {
const TARGET = '/prod-api/system/user/getUserList'
const TOKEN = '你的token,不要带 Bearer 前缀'

const oldOpen = XMLHttpRequest.prototype.open
const oldSend = XMLHttpRequest.prototype.send
const oldSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader

XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
this._matchTarget = false
if (typeof url === 'string' && url.includes(TARGET)) {
this._matchTarget = true
}
return oldOpen.call(this, method, url, async, user, password)
}

XMLHttpRequest.prototype.setRequestHeader = function (name, value) {
return oldSetRequestHeader.call(this, name, value)
}

XMLHttpRequest.prototype.send = function (body) {
if (this._matchTarget) {
oldSetRequestHeader.call(this, 'Authorization', `Bearer ${TOKEN}`)
console.log('[XHR已注入Authorization]')
}
return oldSend.call(this, body)
}
console.log('XHR Header 拦截器已生效')
})()

模板 3:XHR 同时改 URL + Header

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
(() => {
const TARGET = '/prod-api/system/user/getUserList'
const DEPT_ID = '10001'
const TOKEN = '你的token'

const oldOpen = XMLHttpRequest.prototype.open
const oldSend = XMLHttpRequest.prototype.send
const oldSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader

XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
this._matchTarget = false
if (typeof url === 'string' && url.includes(TARGET)) {
const u = new URL(url, location.origin)
u.searchParams.set('deptId', DEPT_ID)
url = u.toString()
this._matchTarget = true
console.log('[XHR已改写URL]', url)
}
return oldOpen.call(this, method, url, async, user, password)
}

XMLHttpRequest.prototype.setRequestHeader = function (name, value) {
return oldSetRequestHeader.call(this, name, value)
}

XMLHttpRequest.prototype.send = function (body) {
if (this._matchTarget) {
oldSetRequestHeader.call(this, 'Authorization', `Bearer ${TOKEN}`)
console.log('[XHR已注入Authorization]')
}
return oldSend.call(this, body)
}
console.log('XHR URL + Header 拦截器已生效')
})()

模板 4:XHR 改 POST Body

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
(() => {
const TARGET = '/prod-api/system/user/getUserList'

const oldOpen = XMLHttpRequest.prototype.open
const oldSend = XMLHttpRequest.prototype.send

XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
this._matchTarget = typeof url === 'string' && url.includes(TARGET)
return oldOpen.call(this, method, url, async, user, password)
}
XMLHttpRequest.prototype.send = function (body) {
if (this._matchTarget && typeof body === 'string') {
try {
const data = JSON.parse(body)
data.deptId = 10001
body = JSON.stringify(data)
console.log('[XHR已改写Body]', body)
} catch (e) {
console.log('[XHR] body 不是 JSON,未处理')
}
}
return oldSend.call(this, body)
}
console.log('XHR Body 拦截器已生效')
})()

axios + XHR 双保险模板

适用于:

  • 不确定请求到底是 axios 还是原生 XHR
  • 页面逻辑复杂
  • 既想在源码层拦,也想在浏览器层兜底
  • 联调时追求稳定命中

模板 1:axios + XHR 同时追加 URL 参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// ===== axios 拦截 =====
if (window.axios) {
window.axios.interceptors.request.use(config => {
if (config.url && config.url.includes('/prod-api/system/user/getUserList')) {
config.params = {
...(config.params || {}),
deptId: 10001
}
console.log('[axios已改写params]', config.url)
}
return config
})
}

// ===== XHR 拦截 =====
(() => {
const TARGET = '/prod-api/system/user/getUserList'
const DEPT_ID = '10001'
const oldOpen = XMLHttpRequest.prototype.open

XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
if (typeof url === 'string' && url.includes(TARGET)) {
const u = new URL(url, location.origin)
u.searchParams.set('deptId', DEPT_ID)
url = u.toString()
console.log('[XHR已改写URL]', url)
}
return oldOpen.call(this, method, url, async, user, password)
}
console.log('axios + XHR URL 双拦截已生效')
})()

模板 2:axios + XHR 同时追加 Authorization

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// ===== axios 拦截 =====
if (window.axios) {
window.axios.interceptors.request.use(config => {
if (config.url && config.url.includes('/prod-api/system/user/getUserList')) {
config.headers = {
...(config.headers || {}),
Authorization: '你的token'
}
console.log('[axios已注入Authorization]', config.url)
}
return config
})
}

// ===== XHR 拦截 =====
(() => {
const TARGET = '/prod-api/system/user/getUserList'
const TOKEN = '你的token'

const oldOpen = XMLHttpRequest.prototype.open
const oldSend = XMLHttpRequest.prototype.send
const oldSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader

XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
this._matchTarget = typeof url === 'string' && url.includes(TARGET)
return oldOpen.call(this, method, url, async, user, password)
}

XMLHttpRequest.prototype.setRequestHeader = function (name, value) {
return oldSetRequestHeader.call(this, name, value)
}

XMLHttpRequest.prototype.send = function (body) {
if (this._matchTarget) {
oldSetRequestHeader.call(this, 'Authorization', `${TOKEN}`)
console.log('[XHR已注入Authorization]')
}
return oldSend.call(this, body)
}
console.log('axios + XHR Header 双拦截已生效')
})()

模板 3:axios + XHR 全量模板(URL + Header + Body)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// ===== axios 拦截 =====
if (window.axios) {
window.axios.interceptors.request.use(config => {
if (config.url && config.url.includes('/prod-api/system/user/getUserList')) {
config.params = {
...(config.params || {}),
deptId: 10001
}
config.headers = {
...(config.headers || {}),
Authorization: '你的token'
}
if (config.data && typeof config.data === 'string') {
try {
const data = JSON.parse(config.data)
data.deptId = 10001
config.data = JSON.stringify(data)
} catch (e) {}
} else if (config.data && typeof config.data === 'object') {
config.data = {
...config.data,
deptId: 10001
}
}
console.log('[axios已拦截]', config.url)
}

return config
})
}

// ===== XHR 拦截 =====
(() => {
const TARGET = '/prod-api/system/user/getUserList'
const DEPT_ID = '10001'
const TOKEN = '你的token'

const oldOpen = XMLHttpRequest.prototype.open
const oldSend = XMLHttpRequest.prototype.send
const oldSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader

XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
this._matchTarget = false
if (typeof url === 'string' && url.includes(TARGET)) {
const u = new URL(url, location.origin)
u.searchParams.set('deptId', DEPT_ID)
url = u.toString()
this._matchTarget = true
console.log('[XHR已改写URL]', url)
}
return oldOpen.call(this, method, url, async, user, password)
}

XMLHttpRequest.prototype.setRequestHeader = function (name, value) {
return oldSetRequestHeader.call(this, name, value)
}

XMLHttpRequest.prototype.send = function (body) {
if (this._matchTarget) {
oldSetRequestHeader.call(this, 'Authorization', `${TOKEN}`)
if (typeof body === 'string') {
try {
const data = JSON.parse(body)
data.deptId = 10001
body = JSON.stringify(data)
console.log('[XHR已改写Body]', body)
} catch (e) {}
}
console.log('[XHR已注入Authorization]')
}
return oldSend.call(this, body)
}
console.log('axios + XHR 全量双拦截已生效')
})()

推荐的通用可复用模板

如果你后面要经常套用,建议把参数抽出来。

1
2
3
const TARGET = '/prod-api/system/user/getUserList'
const DEPT_ID = '10001'
const TOKEN = '你的token'

以后只改这三个变量:

  • TARGET:目标接口
  • DEPT_ID:要注入的参数
  • TOKEN:要注入的 token

Chrome 控制台注入步骤

方式 1:临时调试

  1. 打开页面
  2. F12
  3. 切到 Console
  4. 粘贴拦截脚本
  5. 回页面重新触发请求

方式 2:Sources -> Snippets 持久复用

如果你经常要用,可以把脚本存成 Snippet,这样比每次复制粘贴更方便。

  1. 打开 Chrome DevTools
  2. 进入 Sources
  3. 找到 Snippets
  4. 新建一个脚本
  5. 保存这套模板
  6. 以后右键直接运行


常见问题

为什么脚本执行了,但请求没改掉?

常见原因:

脚本执行太晚,请求已经发出去了

页面不是用 XHR,而是用 fetch

请求参数不在 URL,而在 body

请求逻辑在 iframe 或 worker 中

页面源码里又把参数覆盖回去了


为什么 URL 追加参数后接口还是没生效?

可能是后端根本不认这个参数。例如:后端可能不是从 @RequestParam("deptId") 取值,而是从这里面取部门信息,这时你前端加了 deptId 也没用。

  • 登录用户信息
  • token
  • session
  • 请求头
  • 网关上下文


axios 拦截和 XHR 拦截该选哪个?

选 axios

适合项目源码里改,最规范。

选 XHR

适合浏览器临时调试,最快。

选 axios + XHR

适合你不确定页面请求路径、想提高命中率。


结论

如果你只是临时调试,优先用:

  • XHR 拦截模板
    如果你要在项目里长期使用,优先用:
  • axios 拦截模板
    如果你不确定页面底层实现,或者想双保险,直接用:
  • axios + XHR 模板
    后续复用时,只需要替换这几个值,即可快速套用。
    1
    2
    3
    const TARGET = '你的目标接口'
    const DEPT_ID = '你的参数值'
    const TOKEN = '你的token'