面试题答案
一键面试预检请求触发条件
- 请求方法:使用了
PUT
、DELETE
、CONNECT
、OPTIONS
、TRACE
或者PATCH
这些非简单请求方法,除了常见的GET
和POST
(且POST
方法的Content-Type
必须是application/x-www-form-urlencoded
、multipart/form-data
或text/plain
之外的类型)。 - 自定义头部:设置了自定义的请求头,例如
X-Custom-Header: value
这样非标准的头部字段。
预检请求作用
- 安全验证:在真正发送复杂请求之前,浏览器先发送预检请求到服务器,询问服务器是否允许该复杂请求。服务器通过预检请求可以验证请求的合法性,防止恶意请求,保护服务器资源安全。
- 确定服务器支持:让服务器告知浏览器,后续真正的复杂请求可以使用哪些 HTTP 方法、哪些自定义头部等,使得浏览器和服务器之间达成关于复杂请求的一致,确保后续请求能够成功。
前端处理
- 无需特殊处理:现代浏览器会自动处理预检请求,开发者一般无需额外编写代码来发送预检请求。在发送复杂请求时,按照正常的
fetch
或者XMLHttpRequest
方式编写代码即可。例如使用fetch
:
fetch('https://example.com/api', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
},
body: JSON.stringify({ data: 'example' })
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
- 处理响应:在复杂请求成功后,按照正常的响应处理逻辑,如解析响应数据、处理错误等。
后端处理
- 设置响应头:在服务器端,需要正确处理预检请求(
OPTIONS
方法),并返回合适的响应头,告知浏览器允许的请求方法、允许的头部字段等。以 Node.js 为例,使用express
框架:
const express = require('express');
const app = express();
app.options('*', (req, res) => {
res.set('Access-Control-Allow-Origin', '*');
res.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.set('Access-Control-Allow-Headers', 'Content-Type, X-Custom-Header');
res.status(200).send();
});
// 处理实际的复杂请求
app.put('/api', (req, res) => {
// 处理请求逻辑
res.send('Success');
});
const port = 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
- 处理复杂请求:在处理实际的复杂请求(如
PUT
、DELETE
等)时,按照正常的业务逻辑处理请求,并返回相应的响应。
复杂请求场景举例
假设前端要向服务器上传一个 JSON 格式的数据,并且设置了一个自定义头部 X-User-Id
来标识用户。
- 前端代码:
fetch('https://example.com/api/upload', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-User-Id': '12345'
},
body: JSON.stringify({ content: 'example data' })
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
- 该场景触发预检请求原因:
- 请求方法:虽然使用
POST
方法,但Content-Type
是application/json
,不属于简单请求的Content-Type
类型。 - 自定义头部:设置了自定义头部
X-User-Id
。所以会触发预检请求(OPTIONS
),确保服务器允许这样的请求。
- 请求方法:虽然使用