Angular 9 : Unable to change or modify HTTP headers

标签 angular http-headers lumen preflight http-options-method

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

现在,对于 UI,我使用 Angular 9 和 Material 布局。我有一个非常简单的登录组件:它基本上验证输入,然后调用与我的 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 中,我可以在第三个参数中传递附加 header ,并且我已经尝试过这样做,但是我面临的问题是,无论我传递给它什么 header ,并且当我调用该请求时,Content-Type 永远不会发送。

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

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 浏览器中,在网络选项卡下的控制台中,当我尝试查看请求 header 时,当应用上面的这些情况时,它会显示显示临时 header - 我找到了一些 Stack Overflow 答案在这个问题上,但他们都没有解决我的问题。

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


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

最佳答案

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

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

A CORS preflight request is a CORS request that checks to see if the CORS protocol is understood and a server is aware using specific methods and headers.

It is an OPTIONS request, using three HTTP request headers: Access-Control-Request-Method, Access-Control-Request-Headers, and the Origin header.

这意味着在 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;
    }
}

然后这个中间件被正确注入(inject)到 bootstrap/app.php 中:

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

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

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

原因是routes/web.php没有处理任何OPTIONS请求,所以这是由网络服务器本身处理的,因为我将 Apache 与 .htaccess 一起使用,这也导致了一些小问题,但我不想坚持让 Apache 处理这个问题。我想保持一个约定,即大多数事情都是由 Lumen/Laravel 处理的。

因此,为了让我的 CorsMiddleware 捕获 OPTIONS 请求,我通过将其添加到我的主 routes/web.xml 之上来解决整个问题。 php 文件:

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

现在,CorsMiddleware 中前面提到的 OPTIONS 案例从未发生过,是怎么发生的,这解决了我的整个问题。

关于 Angular 9 : Unable to change or modify HTTP headers,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60824263/

相关文章:

laravel - Lumen 5.2+ 中的 View

http - Lumen $request->all() 返回空数组

javascript - md-form-field 不是已知元素 Angular Material

java - 如何在 Angular 5中未经授权使用主页中的实体数据

javascript - 如何将html内容附加到iframe中?

rest - HTTP header 字段警告用法

javascript - 如何使用 TypeScript 在 Angular 2 的浏览器中获取当前 url?

php - 使用 CURL 检索 header 字符串后,如何在 PHP 中检索各个 header 字段值?

http-headers - 无法删除响应 header 中的服务器(Amazon AWS)

php - 在 Lumen 中设置 OAuth2