Angular调用第三方API遇CORS错误:缺少Access-Control-Allow-Origin头
问题描述
调用API https://api.alternative.me/fng/?limit=1&format=csv&date_format=us 获取数据时遭遇CORS错误,完整错误信息:
Access to XMLHttpRequest at 'https://api.alternative.me/fng/?limit=1&format=csv&date_format=us' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
现有代码
服务类代码
const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' , }) }; @Injectable({ providedIn: 'root' }) export class CryptoCoinsService { constructor(private http: HttpClient) { } fng: string = 'https://api.alternative.me/fng/?limit=1&format=csv&date_format=us'; getFng(): Observable<any[]> { return this.http.get<any[]>(this.fng, httpOptions); } }
组件代码
ngOnInit(){ this.getFng(); } getFng() { this.cryptoCoinService.getFng() .subscribe((data:any) => { this.fng = data; }) }
同一项目中调用CoinGecko API可正常工作,相关代码:
CoinGecko服务类代码
api: string = 'https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=20&page=1&sparkline=false'; getCoinData(): Observable<Coin[]> { return this.http.get<Coin[]>(this.api, httpOptions); }
CoinGecko组件代码
ngOnInit(): void { this.getTopCoins(); } getTopCoins() { this.cryptoCoinService.getCoinData() .subscribe(data => { this.topCoins = data; }); }
尝试过以下代理配置,但不确定是否正确:
{ "/fng":{ "target": "https://api.alternative.me", "secure": false, "logLevel": "debug", "changeOrigin": true } }
疑问:错误提示缺少Access-Control-Allow-Origin头,但已在请求头设置该字段为*,请问哪里操作错误,该如何解决?
错误原因分析
- 请求头设置完全无效:
Access-Control-Allow-Origin是服务端返回的响应头,不是客户端请求头,你在请求里添加这个字段对解决CORS问题没有任何帮助。 - API跨域策略差异:CoinGecko的API配置了允许跨域的响应头,所以能正常访问;而Alternative.me的这个API未配置相关响应头,导致浏览器拦截请求。
- 代理配置未生效:你编写了代理规则,但没有修改API请求路径,代理根本没起作用。
解决步骤
步骤1:修正请求头配置
删掉httpOptions里的Access-Control-Allow-Origin字段,同时注意你请求的是CSV格式,不需要设置Content-Type: application/json:
// 修正后的请求头配置 const httpOptions = { headers: new HttpHeaders({ // CSV请求无需application/json,可留空或设置为text/csv // 'Content-Type': 'text/csv' }) };
步骤2:正确配置并启用代理
- 修改代理配置文件(通常为
proxy.conf.json):
{ "/api/fng": { "target": "https://api.alternative.me", "secure": true, // 目标为HTTPS,设为true更安全 "logLevel": "debug", "changeOrigin": true, "pathRewrite": { "^/api/fng": "/fng" } // 路径重写,将前缀替换为实际API路径 } }
- 修改服务类中的API路径,使用代理前缀:
// 不再直接调用第三方API,改用代理地址 fng: string = '/api/fng/?limit=1&format=csv&date_format=us';
- 指定代理配置启动项目:
修改package.json的启动命令:
"scripts": { "start": "ng serve --proxy-config proxy.conf.json" }
执行npm start启动项目,代理即可生效。
步骤3:处理CSV格式响应
HttpClient默认会将响应解析为JSON,但你请求的是CSV,需要指定响应类型为文本,之后手动解析:
// 服务类中修改get请求的响应类型 getFng(): Observable<string> { return this.http.get(this.fng, { ...httpOptions, responseType: 'text' }); }
组件中订阅并解析CSV:
getFng() { this.cryptoCoinService.getFng() .subscribe((data:string) => { // 简单解析CSV字符串 const rows = data.split('\n').filter(row => row); const headers = rows[0].split(',').map(h => h.trim()); this.fng = rows.slice(1).map(row => { const values = row.split(',').map(v => v.trim()); return headers.reduce((obj, header, index) => { obj[header] = values[index]; return obj; }, {} as any); }); }) }
内容的提问来源于stack exchange,提问作者Marrs

