Angular 9:无法更改或修改 HTTP 标头

Posted

技术标签:

【中文标题】Angular 9:无法更改或修改 HTTP 标头【英文标题】:Angular 9: Unable to change or modify HTTP headers 【发布时间】:2020-07-04 13:09:09 【问题描述】:

我的 API 服务器在 Lumen 框架上运行,并且我在那里安装了一个 CORS 中间件,并附带一个管理 JSON POST 数据的中间件。在服务器端,一切都很完美。

现在,对于 UI,我使用 Angular 9 和 Material 布局。我有一个非常简单的 login 组件:它基本上验证输入,然后调用与我的 API 服务器通信的服务 AuthService

AuthService 非常简单:

import  Injectable  from '@angular/core';
import  Router  from '@angular/router';
import  BehaviorSubject, Observable, throwError  from 'rxjs';
import  catchError, map  from 'rxjs/operators';
import  HttpClient, HttpHeaders, HttpErrorResponse, HttpParams  from '@angular/common/http';

export interface Token 
  token: string;
  token_type: string;
  expires_in: number;


@Injectable(
  providedIn: 'root'
)
export class AuthService 
  public isAuthenticated = new BehaviorSubject<boolean>(false);

  constructor(
    private http: HttpClient,
    private router: Router
  ) 

  async checkAuthenticated() 
    const authToken = localStorage.getItem('access_token');
    return (authToken !== null);
  

  getToken() 
    return localStorage.getItem('access_token');
  

  async login(username: string, password: string) 
    const api = 'http://api.elektron.test/v1/login';
    const headers = new HttpHeaders('Content-Type': 'application/json; charset=UTF-8');

    const body = JSON.stringify(
      email: username,
      password
    );

    // tslint:disable-next-line:max-line-length
    return this.http.post(api, body)
      .subscribe((res: any) => 
        if(res.token) 
          this.isAuthenticated.next(true);
          localStorage.setItem('access_token', res.token);
        
      , error => 
        console.error(error);
      , () => 
        console.log('etstamente');
      );
  

  async logout(redirect: string) 
    const removeToken = localStorage.removeItem('access_token');

    if (removeToken == null) 
      this.isAuthenticated.next(false);
      this.router.navigate([redirect]);
    
  

  handleError(error: HttpErrorResponse) 
    let msg = '';
    if (error.error instanceof ErrorEvent) 
      // client-side error
      msg = error.error.message;
     else 
      // server-side error
      msg = `Error Code: $error.status\nMessage: $error.message`;
    
    return throwError(msg);
  

this.http.post 中,我可以在第三个参数中传递额外的标头,我已经尝试过了,但是我面临的问题是,无论我传入哪个标头,当我调用该请求时,@987654323 @ 永远不会发送。

我尝试的另一个想法是使用拦截器:

import HttpInterceptor, HttpRequest, HttpHandler, HttpHeaders from '@angular/common/http';
import  AuthService  from './auth.service';

@Injectable()

export class AuthInterceptor implements HttpInterceptor 
  constructor(private authService: AuthService)  

  intercept(req: HttpRequest<any>, next: HttpHandler) 
    const token = this.authService.getToken();

    req = req.clone(
      responseType: 'json'
    );

    if (token) 
      req = req.clone( headers: req.headers.set('Authorization', 'Bearer ' + token) );
    

    if (!req.headers.has('Content-Type')) 
      req = req.clone(
        setHeaders: 
          'Content-Type': 'application/json',
        
      );
    

    // setting the accept header
    req = req.clone( headers: req.headers.set('Accept', 'application/json') );

    return next.handle(req);
  

当它到达那里的任何 req.clone 语句时,我最终的行为与之前对 POST 请求的解释相同。

所以,我不知道我在这里做错了什么或导致这种情况的原因。在 Chrome 浏览器中,当我尝试查看请求标头时,在网络选项卡下的控制台中,当应用上述这些情况时,它会声明 显示临时标头 - 我找到了一些 Stack Overflow 答案在那个问题上,但他们都没有解决我的问题。

我花了 5-6 个小时在网上搜索解决方案,尝试了一些我的想法,但都无济于事。


编辑:此问题与 Angular 无关,而与服务器和后端配置以及处理预检请求有关。

【问题讨论】:

【参考方案1】:

经过一个不眠之夜,我终于解决了这个问题,这不是Angular或我的代码的问题,而是Web服务器的配置问题。

如果有人遇到类似的情况,这个答案可能会提供一个潜在的解决方案,这是一个preflight request 的书籍示例:

CORS 预检请求是一个 CORS 请求,用于检查是否 理解 CORS 协议并且服务器知道使用特定的 方法和标头。

这是一个 OPTIONS 请求,使用三个 HTTP 请求标头: Access-Control-Request-Method、Access-Control-Request-Headers 和 源头。

这意味着在 Angular 应用程序发出每个请求之前,还有另一个请求会通过 OPTIONS 请求针对服务器检查 CORS 选项。

我的服务器设置和 Lumen/Laravel CORS 设置一直失败。这就是我的CorsMiddleware 从一开始的样子:

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

/**
 * Class CorsMiddleware
 * @package App\Http\Middleware
 */
class CorsMiddleware

    /**
     * Handle an incoming request.
     *
     * @param Request $request
     * @param Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    
        $headers = [
            'Access-Control-Allow-Credentials' => 'true',
            'Access-Control-Max-Age' => '86400',
            'Access-Control-Allow-Methods' => 'POST, GET, OPTIONS, PUT, PATCH, HEAD, DELETE',
            'Access-Control-Allow-Headers' => 'X-Requested-With, Content-Type, Accept, Origin, Authorization',
            'Access-Control-Expose-Headers' => 'X-Requested-With, Content-Type, Accept, Origin, Authorization',
            'Access-Control-Allow-Origin' => '*',
        ];

        if ($request->isMethod('OPTIONS')) 
            return response()->json(['method' => 'OPTIONS'], 200, $headers);
        

        $response = $next($request);

        foreach($headers as $key => $value) 
            $response->header($key, $value);
        

        return $response;
    

然后这个中间件被正确注入bootstrap/app.php 为:

$app->routeMiddleware([
    'cors' => App\Http\Middleware\CorsMiddleware::class,
    ...,
]);

当这个中间件运行时,所有的标头都在请求响应中发送,但是,CorsMiddleware 中的这条语句从未达到这个条件,即使我尝试使用 Postman:

if ($request->isMethod('OPTIONS')) 
    return response()->json(['method' => 'OPTIONS'], 200, $headers);

原因是 routes/web.php 没有处理任何 OPTIONS 请求,所以这是由 Web 服务器本身处理的,并且由于我使用 Apache 和 .htaccess,这也导致了一些小问题,但我不想坚持让 Apache 处理这个问题。我想保持一个约定,大部分事情都由 Lumen/Laravel 处理。

所以,为了让我的 CorsMiddleware 捕获 OPTIONS 请求,我解决了整个问题,将其添加到我的主要 routes/web.php 文件之上:

Route::options('/any:.*', ['middleware' => ['cors']]);

现在CorsMiddleware 中之前提到的OPTIONS 案例从未发生过,如何解决,这解决了我的整个问题。

【讨论】:

【参考方案2】:

这可能是问题所在 req = req.clone( 响应类型:'json' );

尝试将请求克隆到一个新的变量中,例如: var newReq = req.clone( 响应类型:'json' );

....其余代码... 然后返回 newReq。

【讨论】:

以上是关于Angular 9:无法更改或修改 HTTP 标头的主要内容,如果未能解决你的问题,请参考以下文章

我无法在 Angular 4 上向 laravel api 发送带有标头的 http 请求

Angular 2 http 标头似乎生成或发送不正确

Angular 无法从 HttpEvent 标头获取 ETag 标头

如何在 Spring Boot 中更改允许的标头

Angular JS HTTP Response标头无法在Safari上运行

Angular:自定义标头被 $http 和 $resource 忽略。为啥?