使用 Angular HttpClient 阅读整个响应,包括 headers

Read whole response including headers with Angular HttpClient

我正在尝试使用 spring 引导和 angular 实现基于令牌的身份验证。身份验证令牌暴露给具有相同名称令牌的 HTTP 响应 header。在 angular 中,我在我的服务文件中对 api 提出了以下请求:

login(username: string, password: string): Observable<any> {

    return this.http.post(AUTH_API + 'login', {
      username,
      password

    }, httpOptions);
  }

以及以下组件 ts 方法:

onSubmit(): void {
    const { username, password } = this.form;
     this.authService.login(username, password).subscribe({
      next: data => {
       console.log(data.headers.get('token'));
        //this.tokenStorage.saveToken(data.accessToken);

        this.tokenStorage.saveUser(data);

        this.isLoginFailed = false;
        this.isLoggedIn = true;
        this.roles = this.tokenStorage.getUser().roles;

        this.reloadPage();
      },


      error: err => {
     //  console.log( this.tokenStorage.getUser().roles);
       console.log("ERR");
        this.errorMessage = err.error.message;
        this.isLoginFailed = true;
      }
    });

  }

当我尝试在控制台中显示令牌 header 时,出现以下错误: TypeError:无法读取 null 的属性(读取 'headers') 这是我在安全配置 spring 启动时的 cors 配置 class:

 CorsConfigurationSource corsConfigurationSource() {
            final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
           
            source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
           // source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
            CorsConfiguration conf=   new CorsConfiguration().applyPermitDefaultValues();
            conf.setExposedHeaders(Arrays.asList("token"));
             source.registerCorsConfiguration("/**", conf);
                
            return source;
        

令牌 header 在邮递员或 chrome consol 中可见,但在 angular 前端中不可见。我错过了什么? HTTP 选项

protected void configure(HttpSecurity http) throws Exception {
            http.cors().and().csrf().disable().authorizeRequests()
                    .antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
                    .anyRequest().authenticated()
                    .and()
                    .addFilter(new AuthenticationFilter(authenticationManager()))
                    .addFilter(new AuthorizationFilter(authenticationManager()))
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }

Angular http 选项

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

Headers

Request URL: http://localhost:8080/login
Request Method: POST
Status Code: 200 
Remote Address: [::1]:8080
Referrer Policy: strict-origin-when-cross-origin
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: token
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Connection: keep-alive
Content-Length: 0
Date: Sat, 29 Jan 2022 22:06:39 GMT
Expires: 0
Keep-Alive: timeout=60
Pragma: no-cache
token: eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0MTAiLCJleHAiOjE2NDM1MDU5OTl9.xtZxAU-TbpUmWFm_CuKS7iCHu-FiBs332cCVlxca6AwY4XOz-K7GJ4yB8PLLjk1JuXcgs1sbcVHeBto7iW7rpA
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7
Connection: keep-alive
Content-Length: 41
Content-Type: application/json
Host: localhost:8080
Origin: http://localhost:8081
Referer: http://localhost:8081/
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36

您必须在选项中设置需要观察的部分:

const httpOptions = {
    headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
    observe: 'response'
};
return this.http.post(AUTH_API + 'login', {
    username,
    password
}, httpOptions);

这将为您提供完整的响应,以便订阅回调中的数据参数将包含 headersbody