从 Angular 中的 REST API 获取 HTTPS 时出错

Posted

技术标签:

【中文标题】从 Angular 中的 REST API 获取 HTTPS 时出错【英文标题】:Error when GET HTTPS from REST API in Angular 【发布时间】:2020-08-11 04:46:09 【问题描述】:

我有一个 Angular 应用程序正在运行,它使用外部 api 来获取国家/地区 ISO。 此 API 使用 https,它给了我一个错误。

问题是:当我在我的 Angular 本地环境中使用代理时,将 /iso-api/ 映射到真实的 url 就可以了。

"/iso-api/*": 
    "target": "https://www...",
    "pathRewrite":  "^/iso-api": "" ,
    "secure": false,
    "changeOrigin": true,
    "logLevel": "debug"

但我希望它在生产中工作,所以我想使用真实的 url。

在我的服务器中,我已经返回了 Access-Control-Allow-Origin: * 标头。

我尝试使用 ssl 运行 angular 服务器(因为外部 api 使用 https),但我收到了同样的错误。

我知道一个解决方案是在服务器中实现代理,但我认为不应该这样做,并且可能有一种方法可以从前端检索这些数据。 请帮忙。

响应

这是 Chrome 中的网络错误:

Firefox 中,请求以 200 OK 结束并返回数据,但抛出 CORS 错误,我无法从应用程序访问数据:CORS header 'Access-Control-Allow-Origin' missing

常规

Request URL: https://www...
Referrer Policy: no-referrer-when-downgrade

请求标头

:method: GET
:scheme: https
accept: application/json, text/plain, */*
accept-encoding: gzip, deflate, br
accept-language: es-ES,es;q=0.9,en;q=0.8
origin: http://localhost:4200
referer: http://localhost:4200/app/login
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site

响应标头

accept-ranges: bytes
cache-control: max-age=0
content-encoding: gzip
content-language: en-US
content-length: 68356
content-type: application/json
date: Mon, 27 Apr 2020 14:49:30 GMT
expires: Mon, 27 Apr 2020 14:49:30 GMT
referrer-policy: strict-origin-when-cross-origin
server-timing: cdn-cache; desc=HIT
server-timing: edge; dur=1
server-timing: ACTT;dur=0,ACRTT;dur=88
set-cookie: ... expires=Mon, 27 Apr 2020 16:49:30 GMT; max-age=7200; path=/; domain=...; HttpOnly
set-cookie: ... Domain=...; Path=/; Expires=Mon, 27 Apr 2020 18:49:30 GMT; Max-Age=14400; HttpOnly
set-cookie: ... Domain=...; Path=/; Expires=Tue, 27 Apr 2021 14:49:30 GMT; Max-Age=31536000; Secure
status: 200
vary: Accept-Encoding

更新

Angular 服务代码

import  HttpClient  from '@angular/common/http';

...

constructor(
    private _http: HttpClient,
    private _errorUtil: ErrorUtilService,
    private _converter: StoreConverter
  ) 

...
  getCountries(): Observable<CountryWithLanguages[]> 
    return this._http.get<GetStoresResponse>(API.storeUrl).pipe(
      catchError(this._errorUtil.handle),
      map(result => result.stores),
      switchMap(stores => stores),
      filter(this._isActiveStore),
      map(store => this._converter.toView(store)),
      toArray()
    );
  

为了为我使用 Angular 开发服务器的应用程序提供服务,我没有手动添加“Access-Control-Allow-Origin”标头,但是在浏览器中,我看到它正在被添加。

angular.json

"serve": 
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": 
            "browserTarget": "push-web-app:build",
            "proxyConfig": "src/proxy-local.conf.json"
          ,
         

【问题讨论】:

那么您是从本地服务器调用生产 API 吗?出现 err_failed 错误但仍然有响应标头有点奇怪。您在控制台中没有其他错误? 那么可能是愚蠢的问题,但是您是否尝试过使用 fetch 或 ajax?只是想看看您是否可以查明问题的确切位置? 这是一个开放的 API,而不是我管理的生产应用程序,它在从浏览器访问 url 时工作。控制台中没有错误,只有网络选项卡中的消息......我尝试使用 fetch 但同样的情况发生了。我认为问题不在于 http get 请求,而在于浏览器限制 cors 您可能想尝试使用 Firefox devtools 来检查请求——因为不幸的是,在许多不同的情况下,Chrome devtools 不再公开某些请求和响应详细信息。例如,请参阅***.com/q/57410051/441757。 所以你的问题的答案是你需要在你的远程服务器的生产环境中设置你自己的代理。如果您不拥有 API 服务器,这是绕过 CORS 的唯一方法。您使用的是哪种网络服务器? nginx 【参考方案1】:

您不能从其他域请求资源。这将是一个安全漏洞。你可以在这里阅读更多:Same-origin policy

从您的服务器发送 Access-Control-Allow-Origin: * 不会让您访问上述 API。这个API的提供者需要给你访问这个API的权限,你不能给自己这个权限。

您发布的错误表明缺少Access-Control-Allow-Origin 标头。这意味着 API 没有发送此标头。

此 API 未发送 Access-Control-Allow-Origin 标头可能有两个原因。

    此 API 方面的配置错误。在这种情况下,您必须请求此 API 的提供者来解决此问题。 API 提供商故意限制对 API 的访问。在这种情况下,您必须要求此 API 的提供者允许您从您的域访问。

您还可以通过您的服务器代理请求。使用代理时的核心区别在于您的服务器从 API 而不是客户端浏览器读取资源。参见David's response 了解如何使用 nginx 配置代理。

【讨论】:

【参考方案2】:

您无法绕过 CORS 浏览器端。如果您无法修改服务器端,您唯一的解决方案是使用代理。

出于开发目的,您可以使用 Angular 的内置代理服务器,但不能用于生产。

这是执行此操作的基本 nginx 配置

server 
    listen          80;
    server_name     yourdomain.com; #domain where your angular code is deployed

    location /iso-api

        RewriteRule                     ^/iso-api/(.*)$ /$1 break;
        proxy_pass                      https://thirdpartyapidomain.com; #url of the API you are trying to access
    

    location
    
        #Your normal angular location
        #try_files ...
    

这将重定向请求,如

http://yourdomain.com/iso-api/countriesListhttps://thirdpartyapidomain.com/countriesList;

由于现在客户端和服务器 API 调用在同一个域中,因此您不应该遇到 CORS 问题

【讨论】:

确切地说,你应该说“你不能绕过同源策略浏览器端”而不是“你不能绕过 CORS 浏览器端”。 CORS 是一种放宽同源政策的方法。【参考方案3】:

尝试使用选项 withCredentials: true 的 .get 调用:

return this._http.get<GetStoresResponse>(API.storeUrl,  withCredentials: true ).pipe();

...和/或确保您的浏览器是最新的。

【讨论】:

请求不需要授权。反正我试过了,还是不行。 我想,但在其他情况下,通过 withCredentials 传递所需证书的情况有一些先例——值得一试。祝你好运!【参考方案4】:

使用此站点解决 CORS 错误: https://cors-anywhere.herokuapp.com/

使用示例 https://cors-anywhere.herokuapp.com/https://freegeoip.app/json

this._http.get('https://cors-anywhere.herokuapp.com/https://freegeoip.app/json')

这只是一种解决方法,但它确实有效。使用它甚至只是为了了解错误是否与 CORS 或其他有关。

【讨论】:

谢谢,但我需要一个强大的解决方案。我正在尝试在前端找到解决方案。正如我在问题中所写:我知道一种解决方案是在服务器中实现代理,但我认为不应该这样做,并且可能有一种方法可以从前端检索这些数据。

以上是关于从 Angular 中的 REST API 获取 HTTPS 时出错的主要内容,如果未能解决你的问题,请参考以下文章

从 Angular App 到 Django REST API 的 API 请求 - 被 CORS 阻止

如何在 Angular 7 的激活链接中获取 url 参数并从服务类调用 rest api

Angular cors 从 django rest 框架后端获取数据并获得许可

在角度(离子)上的wp rest api中获取帖子绑定到id

从 Django Rest Framework 中的令牌获取经过身份验证的用户

Angular JS $http.get 从 WP REST API 返回空数据,但仅在实际 iOS 设备上