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

Angular17调用Go Gorilla/Mux API遇CORS错误:响应无HTTP成功状态

CORS错误排查与解决方案:预检请求无HTTP OK状态

错误信息

Access to XMLHttpRequest at 'http://localhost:8082/login' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

问题原因

1. Go后端未正确处理OPTIONS预检请求

浏览器发送跨域请求前会先发送OPTIONS预检请求验证权限,但当前Go中间件仅设置CORS响应头,未专门处理OPTIONS请求:

  • 当路由未定义OPTIONS方法的处理器时,Gorilla/Mux会返回404状态码
  • 预检请求要求必须返回200/204的OK状态,否则浏览器会拦截后续请求

2. Angular拦截器存在两处问题

  • 错误设置请求头:在请求中添加Access-Control-Allow-Origin,该字段是服务器响应头,前端无需设置,浏览器会忽略该请求头,甚至可能干扰预检逻辑
  • 逻辑漏洞:
    • 无token时,router.navigate后未终止请求流,后续代码仍会执行并发送无效请求
    • token过期时,使用subscribe处理刷新token的异步操作,无法正确将更新后的请求流返回给拦截器,导致后续代码覆盖Authorization头,原请求会提前发送

解决方案

针对Go后端:修改CORS中间件处理OPTIONS请求

修改中间件,检测到OPTIONS请求时直接返回200 OK,无需传递到后续路由处理器:

func jsonContentTypeMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // 设置CORS响应头
        w.Header().Set("Content-Type", "application/json")
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, DELETE")
        // 移除重复的Access-Control-Allow-Headers,仅保留允许的请求头
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With")
        
        // 专门处理OPTIONS预检请求,直接返回200状态
        if r.Method == http.MethodOptions {
            w.WriteHeader(http.StatusOK)
            return
        }
        
        next.ServeHTTP(w, r)
    })
}

针对Angular拦截器:修复逻辑与请求头设置

移除无效请求头,修复异步逻辑,确保请求流正确返回:

@Injectable()
export class HttpInterceptor implements HttpInterceptor {
  constructor(private router: Router, private authService: AuthService) {}

  intercept(req: HttpRequest<any>, nextHandler: HttpHandler): Observable<HttpEvent<any>> {
    const authToken = StorageUtil.getAuthToken();
    let request = req.clone({
      setHeaders: {
        "Content-Type": "application/json"
        // 移除Access-Control-Allow-Origin,该字段属于响应头,前端无需设置
      }
    });

    // 无有效token时,跳转登录并终止请求流
    if (!authToken) {
      this.router.navigate(['/auth/login']);
      return throwError(() => new Error('Você precisa estar logado'));
    }

    // token过期时,通过switchMap处理异步刷新逻辑,确保请求流正确传递
    if (StorageUtil.isTokenExpired()) {
      return this.authService.refreshToken().pipe(
        switchMap((resp) => {
          const accessToken = resp.data.accessToken as string;
          const updatedRequest = request.clone({
            setHeaders: {
              Authorization: `Bearer ${accessToken}`
            }
          });
          return nextHandler.handle(updatedRequest);
        }),
        catchError(() => {
          // 刷新失败时跳转登录
          this.router.navigate(['/auth/login']);
          return throwError(() => new Error('Token过期且刷新失败'));
        })
      );
    }

    // token有效,添加Authorization头后发送请求
    request = request.clone({
      setHeaders: {
        Authorization: `Bearer ${authToken}`
      }
    });

    return nextHandler.handle(request);
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 10:35:11