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
相关产品推荐
相关产品推荐

