【问题标题】:Angular 2 REST request HTTP status code 401 changes to 0Angular 2 REST 请求 HTTP 状态码 401 更改为 0
【发布时间】:2016-12-24 06:21:46
【问题描述】:

我正在开发一个 Angular2 应用程序。似乎当我的访问令牌过期时,401 HTTP 状态代码在响应对象中更改为 0 值。我收到 401 Unauthorized 但 ERROR Response 对象的状态为 0。这阻止了我捕获 401 错误并尝试刷新令牌。是什么导致401 HTTP状态码变成了HTTP状态码0?

这是来自 Firefox 控制台的屏幕截图:

这是我的代码:

get(url: string, options?: RequestOptionsArgs): Observable<any> 
{
    //console.log('GET REQUEST...', url);

    return super.get(url, options)
        .catch((err: Response): any =>
        {
            console.log('************* ERROR Response', err);

            if (err.status === 400 || err.status === 422)
            {
                return Observable.throw(err);
            }
            //NOT AUTHENTICATED
            else if (err.status === 401)
            {                   
                this.authConfig.DeleteToken();
                return Observable.throw(err);
            }               
            else
            {
                // this.errorService.notifyError(err);
                // return Observable.empty();
                return Observable.throw(err);
            }
        })
        // .retryWhen(error => error.delay(500))
        // .timeout(2000, new Error('delay exceeded'))
        .finally(() =>
        {
            //console.log('After the request...');
        });
}

此代码驻留在扩展 Angular2 的 HTTP 的自定义 http 服务中,因此我可以在单个位置拦截错误。

在 Google Chrome 中,我收到以下错误消息:

XMLHttpRequest 无法加载 https://api.cloudcms.com/repositories/XXXXXXXXXXXXXXXX/branches/XXXXXXXXXXXXXXXX/nodesXXXXXXXXXXXXXXXX。请求的资源上不存在“Access-Control-Allow-Origin”标头。因此,Origin 'http://screwtopmedia.local.solutiaconsulting.com' 不允许访问。响应的 HTTP 状态代码为 401。

这很令人困惑,因为我在请求中包含了“Access-Control-Allow-Origin”标头。

这是 Google Chrome 中收到的结果图片:

我尝试访问“WWW-Authenticate”响应标头作为捕获 401 的手段。但是,以下代码返回 NULL:

err.headers.get("WWW-Authenticate")

令人费解的是,我遇到了 CORS 问题,因为在提供有效的访问令牌时我没有收到任何 CORS 错误。

如何捕获 401 HTTP 状态代码?为什么 401 HTTP 状态码被改为 0?

感谢您的帮助。

【问题讨论】:

  • 如果 cloudcms.com 没有在 401 响应中设置 Access-Control-Allow-Origin,那么您无能为力。您必须正确地向他们打开支持票以确认这是否是正常行为。如果发生 CORS 错误,我测试的浏览器中的 javascript(FF、Chrome 和 Safari)不会收到任何信息,除了状态 0。Angular2 无法控制它。
  • 问题解决了吗?我的意思是在服务器不发送正确代码的情况下,但在 chrome 控制台上你会看到 401,但 angular 一直给 0 一样的状态?

标签: rest angular cors


【解决方案1】:

该问题与 CORS 请求有关,请参阅 this github issue

请求中没有“Access-Control-Allow-Origin”标头 资源

表示响应头中需要'Access-Control-Allow-Origin'

Angular 没有得到任何状态码,这就是为什么它给你一个0,这是由于浏览器不允许xml parser 解析response,因为headers 无效。

您需要将正确的 CORS 标头附加到您的 error responsesuccess

【讨论】:

  • 那么,来自第 3 方来源的响应也需要在标头中添加“Access-Control-Allow-Origin”?我已将其添加到我的请求中。
  • 这个状态码老实说还是很奇怪,为什么 Google 会说这是一个 CORS 请求问题而不是 Firefox?
  • 这不是 firefox,因为我在 Google Chrome 中遇到了同样的问题
  • @TomSchreck 完全正确
【解决方案2】:

如果 cloudcms.com 没有在 401 响应中设置 Access-Control-Allow-Origin,那么您无能为力。您必须正确地向他们打开支持票以确认这是否是正常行为。

如果发生 CORS 错误,我测试的浏览器(FF、Chrome 和 Safari)中的 JavaScript 将不会收到任何信息,除了状态 0。Angular2 无法控制它。


我创建了一个简单的测试并得到与您相同的结果:

geturl() {
    console.log('geturl() clicked');
    let headers = new Headers({ 'Content-Type': 'application/xhtml+xml' });
    let options = new RequestOptions({ 'headers': headers });
    this.http.get(this.url)
        .catch((error): any => {
            console.log('************* ERROR Response', error);
            let errMsg = (error.message) ? error.message :
                error.status ? `${error.status} - ${error.statusText}` : 'Web Server error';
            return Observable.throw(error);
        })
        .subscribe(
        (i: Response) => { console.log(i); },
        (e: any) => { console.log(e); }
        );
}

经过大量谷歌搜索,我发现以下内容(https://github.com/angular/angular.js/issues/3336):

lucassp 于 2013 年 8 月 19 日发表评论

我发现这个问题已经有一段时间了,但我忘了在这里回复。每个响应,甚至是错误 500,都必须附加 CORS 标头。如果服务器没有将 CORS 标头附加到错误 500 响应,则 XHR 对象不会解析它,因此 XHR 对象内部不会有任何响应正文、状态或任何其他响应数据。

来自 Firefox 网络监视器的结果似乎支持上述原因。

Javascript 请求将收到空响应。

纯 url 请求(复制并粘贴地址栏中的链接)将获得响应正文

Javascript 使用 XHRHttpRequest 进行 http 通信。

当 XHR 回复到达时,浏览器将处理标头,这就是您会看到那些 401 网络消息的原因。但是,如果 XHR 回复来自不同的主机,则 javascript 和响应标头不包含 CORS 标头(例如 Access-Control-Allow-Origin: *),浏览器不会将任何信息传递回 XHR 层。结果,回复正文将完全为空,没有状态(0)。

我用 FF 48.0.1、Chrome 52.0.2743.116 和 Safari 9.1.2 进行了测试,它们都有相同的行为。

使用浏览器网络监视器检查那些401 Unauthorized 条目的响应标头,很可能没有Access-Control-Allow-Origin 标头并导致您面临的问题。这可能是错误或服务提供商方面的设计。

Access-Control-Allow-Origin 是仅响应的 http 标头。更多信息https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#The_HTTP_response_headers

【讨论】:

    【解决方案3】:

    我为 Cloud CMS 工作。我可以确认我们在 API 调用中正确设置了 CORS 标头。但是,对于 401 响应,标头未正确设置。此问题已得到解决,此修复程序将于本周末在公共 API 上提供。

    顺便说一句,如果您使用我们的 javascript 驱动程序 https://github.com/gitana/gitana-javascript-driver 而不是直接写入 API,那么重新验证流程会自动处理,您根本不需要捕获 401 错误。

    【讨论】:

      【解决方案4】:

      根据W3C,如果status属性设置为0,则表示:

      • 状态为 UNSENT 或 OPENED。

      • 错误标志已设置。

      我认为这不是Angular2 问题,您的请求似乎有问题。

      【讨论】:

      • 有趣。你如何解决这个问题?
      • 请求被发送到 REST URL,因为我收到了 401 状态代码。我可以在结果中看到 401 Unauthorized。但是当我收到提供的响应时,状态码为 0。因此,UNSENT 状态似乎不正确。
      • 在我的例子中——Angular 永远不会收到这个请求——它被浏览器阻止,因为缺少返回错误的 CORS 属性。
      • @WitoldKaczurba 是的,你是对的,因为我也这么想,所以我们如何解决这个问题。
      【解决方案5】:

      我遇到了同样的问题,是由于服务器没有发送正确的响应(即使控制台日志显示 401,Angular 错误的状态为 0)。我的服务器是使用 Spring MVC 和 Spring Security 的带有 Java 应用程序的 Tomcat。

      现在使用以下设置工作:

      SecurityConfig.java

      ...
      protected void configure(HttpSecurity http) throws Exception {
      ....
          http.exceptionHandling()
                  .accessDeniedHandler(accessDeniedHandler)
                  .authenticationEntryPoint(authenticationEntryPoint);
          // allow CORS option calls
          http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll();
       ...
      

      SomeAuthenticationEntryPoint.java

      @Component
      public class SomeAuthenticationEntryPoint implements AuthenticationEntryPoint {
      
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
          HttpStatus responseStatus = HttpStatus.UNAUTHORIZED;
          response.sendError(responseStatus.value(), responseStatus.getReasonPhrase());
          }
      }
      

      web.xml

      <error-page>
          <error-code>403</error-code>
          <location>/errors/unauthorised</location>
      </error-page>
      <error-page>
          <error-code>401</error-code>
          <location>/errors/unauthorised</location>
      </error-page>
      

      控制器处理 /errors/... 之前定义的

      @RestController
      @RequestMapping("/errors")
      public class SecurityExceptionController {
      
          @RequestMapping("forbidden")
          public void resourceNotFound() throws Exception {
              // Intentionally empty
          }
      
          @RequestMapping("unauthorised")
          public void unAuthorised() throws Exception {
              // Intentionally empty
              }
      
          }
      }
      

      【讨论】:

      • 感谢您的回复。我正在使用的服务是第 3 方服务。所以,我无法控制/访问他们的服务器。知道为什么我可以在控制台中看到 401 状态代码,而 Angular 中的错误对象的状态代码为 0?这说明问题是 Angular 而不是服务器。
      【解决方案6】:

      您应该使用第 3 方 http 协议监视器(如 CharlesProxy)而不是 Chrome 开发工具来确认哪些标头实际发送到 API 服务以及它是否返回 401。

      【讨论】:

      • 感谢您的回复。我也在使用 Postman,并且可以在提交过期令牌时看到从 3rd 方 API 调用返回的 401 Unauthorized 状态。所以,我认为问题不在于 3rd 方服务。似乎错误响应对象的错误状态在 Angular2 内部的某个地方从 401 变为 0。
      【解决方案7】:

      偶然发现了这个项目:

      我的情况是这样的:

      • 我从不同的域请求 REST
      • 资源返回 401,但错误响应中没有设置“Access-Control-Allow-Origin”。
      • Chrome(或您的浏览器)收到 401 并显示在网络选项卡中(见下文计数:401),但从未传递给 Angular

      • 因为不允许 CORS(缺少“Access-Control-Allow-Origin”标头)- Chrome 无法将此响应传递给 Angular。这样我可以在 Chrome 中看到 401,但在 Angular 中看不到。

      • 解决方案很简单(扩展了 TimothyBrake 的答案) - 将缺少的标头添加到错误响应中。在 Spring boot 中,我输入了:response.addHeader("Access-Control-Allow-Origin", "*");,然后我就被整理出来了。

      @Component
      public class JwtUnauthorizedResponseAuthenticationEntryPoint
        implements AuthenticationEntryPoint {
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response,
                AuthenticationException authException) throws IOException {
      
            response.addHeader("Access-Control-Allow-Origin", "*"); 
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
                    "You would need to provide the Jwt Token to Access This resource");
        }
      }
      

      PS:确保将 bean 提供到 WebSecurityConfigurerAdapter 中的 HttpSecurity 配置中。如有疑问,请参阅:https://www.baeldung.com/spring-security-basic-authentication

      希望这会有所帮助。

      【讨论】:

        猜你喜欢
        • 2017-09-25
        • 1970-01-01
        • 1970-01-01
        • 2019-04-02
        • 1970-01-01
        • 2016-05-11
        • 1970-01-01
        • 1970-01-01
        • 2011-03-18
        相关资源
        最近更新 更多