MST

星途 面试题库

面试题:JavaScript跨域时如何处理复杂请求及预检请求

在JavaScript进行跨域操作时,复杂请求会触发预检请求(OPTIONS)。请详细说明预检请求的触发条件、作用,以及如何在前端和后端分别处理复杂请求和预检请求,以确保跨域操作的正常进行。同时,举例说明一个复杂请求场景。
34.4万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

预检请求触发条件

  1. 请求方法:使用了 PUTDELETECONNECTOPTIONSTRACE 或者 PATCH 这些非简单请求方法,除了常见的 GETPOST(且 POST 方法的 Content-Type 必须是 application/x-www-form-urlencodedmultipart/form-datatext/plain 之外的类型)。
  2. 自定义头部:设置了自定义的请求头,例如 X-Custom-Header: value 这样非标准的头部字段。

预检请求作用

  1. 安全验证:在真正发送复杂请求之前,浏览器先发送预检请求到服务器,询问服务器是否允许该复杂请求。服务器通过预检请求可以验证请求的合法性,防止恶意请求,保护服务器资源安全。
  2. 确定服务器支持:让服务器告知浏览器,后续真正的复杂请求可以使用哪些 HTTP 方法、哪些自定义头部等,使得浏览器和服务器之间达成关于复杂请求的一致,确保后续请求能够成功。

前端处理

  1. 无需特殊处理:现代浏览器会自动处理预检请求,开发者一般无需额外编写代码来发送预检请求。在发送复杂请求时,按照正常的 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));
  1. 处理响应:在复杂请求成功后,按照正常的响应处理逻辑,如解析响应数据、处理错误等。

后端处理

  1. 设置响应头:在服务器端,需要正确处理预检请求(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}`);
});
  1. 处理复杂请求:在处理实际的复杂请求(如 PUTDELETE 等)时,按照正常的业务逻辑处理请求,并返回相应的响应。

复杂请求场景举例

假设前端要向服务器上传一个 JSON 格式的数据,并且设置了一个自定义头部 X-User-Id 来标识用户。

  1. 前端代码
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));
  1. 该场景触发预检请求原因
    • 请求方法:虽然使用 POST 方法,但 Content-Typeapplication/json,不属于简单请求的 Content-Type 类型。
    • 自定义头部:设置了自定义头部 X-User-Id。所以会触发预检请求(OPTIONS),确保服务器允许这样的请求。