You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Fetch请求仅400错误时触发CORS问题求助

解决React POST请求400状态下的CORS问题

这个问题我之前也碰到过,核心原因是你的Slim框架在返回错误响应(比如400)时,没有经过你定义的全局跨域中间件,导致错误响应缺少Access-Control-Allow-Origin头,而正常200响应是走完全部中间件流程的,所以没问题。

下面是具体的排查和解决步骤:

1. 先处理OPTIONS预请求

浏览器在发送POST这类非简单请求时,会先发送一个OPTIONS预请求验证权限,你需要单独处理这个请求,确保它返回带正确跨域头的200响应:

// 处理所有路由的OPTIONS请求
$app->options('/{routes:.+}', function ($request, $response, $args) {
    return $response
        ->withHeader('Access-Control-Allow-Origin', 'http://localhost:3000')
        ->withHeader('Access-Control-Allow-Headers', 'Origin, Content-Type, Accept, X-Auth-Token')
        ->withHeader('Access-Control-Allow-Credentials', 'true')
        ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
});

2. 确保错误响应也带上跨域头

Slim默认的错误处理逻辑会跳过部分中间件,直接生成错误响应,所以你需要自定义错误处理器,给错误响应手动添加跨域头:

// 自定义错误处理器
$app->error(function (\Exception $e, $request, $response) {
    // 获取错误状态码,默认500
    $statusCode = $e->getCode() >= 400 && $e->getCode() < 600 ? $e->getCode() : 500;
    // 生成错误JSON响应
    $errorResponse = $response->withStatus($statusCode)
        ->withHeader('Content-Type', 'application/json')
        ->write(json_encode([
            'error' => $e->getMessage(),
            'code' => $statusCode
        ]));
    
    // 手动添加跨域头
    return $errorResponse
        ->withHeader('Access-Control-Allow-Origin', 'http://localhost:3000')
        ->withHeader('Access-Control-Allow-Headers', 'Origin, Content-Type, Accept, X-Auth-Token')
        ->withHeader('Access-Control-Allow-Credentials', 'true')
        ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
});

同时保留你原来的全局中间件,确保正常响应的跨域头正确:

$app->add(function ($req, $res, $next) {
    $response = $next($req, $res);
    return $response
        ->withHeader('Access-Control-Allow-Origin', 'http://localhost:3000')
        ->withHeader('Access-Control-Allow-Headers', 'Origin, Content-Type, Accept, X-Auth-Token')
        ->withHeader('Access-Control-Allow-Credentials', 'true')
        ->withHeader('Access-Control-Expose-Headers', 'Content-Length, X-Kuma-Revision')
        ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
});

3. 客户端代码优化

不要启用mode: 'no-cors',这个选项会让浏览器返回一个不透明响应,你无法读取响应内容,这就是为什么会出现Unexpected end of input错误。相反,你应该在客户端处理非2xx的响应:

export function loginUsers(userData) {
  return new Promise((resolve, reject) =>{
    fetch(URL,{
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      },
      credentials: 'include',
      body: JSON.stringify(userData),
    })
    .then((response) => {
      // 先检查响应状态,非2xx则解析错误并抛出
      if (!response.ok) {
        return response.json().then(errorData => Promise.reject(errorData));
      }
      return response.json();
    })
    .then((responseJson) =>{
      resolve(responseJson);
    })
    .catch((error) =>{
      reject(error)
    })
  })
}

这样当服务端返回400时,你能拿到具体的错误信息,而不是被CORS错误阻断。

为什么之前200正常、400出错?

当请求正常处理返回200时,响应会完整经过你添加的全局中间件,跨域头被正确添加;但当服务端抛出400错误时,Slim的默认错误处理会直接生成响应,没有经过中间件链,导致跨域头缺失,浏览器就会触发CORS错误。

内容的提问来源于stack exchange,提问作者Liam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:57:24