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

Angular项目CORS错误正规解决方案求助

Angular 项目 CORS 错误排查方案

我正在开发一个Angular课程项目,这是首次接触Angular。已经了解CORS概念及作用,搭建了Node.js服务器,尝试过自定义CORS中间件、cors包不同参数配置,也配置了Angular拦截器。表单数据能正常获取,但始终被CORS错误阻碍,不想用Chrome插件,求正规解决办法。

核心排查与解决方向

  • 单独处理OPTIONS预检请求:浏览器发送实际请求前会先发起OPTIONS预检,自定义中间件需直接返回200响应,不要调用next(),避免后续路由逻辑干扰预检结果。示例修改:
    module.exports = () => (req, res, next) => {
        res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4200');
        res.setHeader('Access-Control-Allow-Credentials', true);
        res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
        res.setHeader("Access-Control-Allow-Headers", 'Content-Type, X-Authorization');
        // 直接处理OPTIONS请求
        if (req.method === 'OPTIONS') {
            return res.sendStatus(200);
        }
        next();
    };
    
  • 避免多CORS配置冲突:不要同时启用自定义CORS中间件和cors包,二者选其一,重复配置会导致响应头混乱,引发浏览器CORS校验失败。
  • Credentials模式禁用通配符:如果设置credentials: true,Access-Control-Allow-Origin必须指定具体域名(如http://localhost:4200),绝对不能用*,否则浏览器会直接拒绝接收响应。
  • 验证请求头权限:确保Angular拦截器添加的X-Authorization头,已包含在后端CORS配置的Access-Control-Allow-Headers列表中。
  • 检查响应头正确性:用浏览器开发者工具的Network面板,查看OPTIONS请求的响应头,确认Access-Control-*系列头是否完整且符合配置。

相关代码

自定义CORS中间件

module.exports = () => (req, res, next) => {
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4200');
    res.setHeader('Access-Control-Allow-Credentials', true);
    res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
    res.setHeader("Access-Control-Allow-Headers", 'Content-Type, X-Authorization');
    next();
};

自定义中间件导入使用

app.use(createdCors())

index.js 直接配置的中间件

app.use((req,res,next) => {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Methods', 'HEAD,OPTIONS,GET,POST,PUT,DELETE');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Authorization');
    next()
})

cors包配置示例

app.use(cors({
    origin: 'http://localhost:4200',
    credentials: true
}))

Angular拦截器代码

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HTTP_INTERCEPTORS } from "@angular/common/http";
import { Injectable, Provider } from "@angular/core";
import { mergeMap, Observable, tap } from "rxjs";

@Injectable()
export class AppInterceptor implements HttpInterceptor{
    accessToken:string | null = localStorage.getItem(`accessToken`)

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> 
    {
       if(this.accessToken)
       {
        return next.handle(req.clone({ setHeaders: {'X-Authorization' : this.accessToken}}))
    }
        else 
        {
            return next.handle(req.clone())
        }
    }
    }

export const AppInterceptorProvider: Provider = {
    provide:HTTP_INTERCEPTORS,
    useClass: AppInterceptor,
    multi:true
}

后端userService相关代码

function createAccessToken(user){
    const payload = { 
        _id: user._id,
        email: user.email,
        username: user.username
    }
    const accessToken = jwt.sign(payload,SECRET_KEY)

    return{
        email: user.email,
        username: user.username,
        accessToken,
        _id:user._id
    }
}

async function register(username,email, password){
    const existingEmail = await User.findOne({email})
    const existingUser = await User.findOne({username})

    if(existingEmail){
        throw new Error(`Email is already taken`)
    }
    else if(existingUser) {
        throw new Error(`User already exists`)
    }

    const user = await User.create({username,email,password})
    return createAccessToken(user)
} 

Register组件代码

import { Component } from '@angular/core';
import {FormBuilder, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { passwordValidator } from 'src/app/shared/validator';
import { AuthService } from '../auth.service';

@Component({
  selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css']
})
export class RegisterComponent {
  
  errors: string | undefined = undefined;
 
  constructor(private fb: FormBuilder, private userService: AuthService, private router: Router) {}
  
  registerForm = this.fb.group({
    email: [``,[Validators.required, Validators.email]],
    username: [``, [Validators.required, Validators.minLength(6)]],
    password: [``, [Validators.required, Validators.minLength(6)]],
    rePass: [``, [Validators.required,passwordValidator]]
  })


  register(): void{
this.userService.register(this.registerForm.value).subscribe
    ({

      next: () => this.router.navigate([`/`]),
      error:(err)=>   {
        this.errors = err.error.error
      }

    })
  
    this.registerForm.reset()
    
  }  
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 14:20:27