Angular 9: невозможно изменить или изменить заголовки HTTP - PullRequest
0 голосов
/ 24 марта 2020

Мой сервер API работает на платформе Lumen, и на нем установлено промежуточное программное обеспечение CORS, а также промежуточное программное обеспечение, которое управляет JSON данными POST. На стороне сервера все идеально.

Теперь для интерфейса я использую Angular 9 с раскладкой материала. У меня есть довольно простой login компонент: он в основном проверяет ввод, а затем вызывает службу AuthService , которая связывается с моим сервером API.

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 я могу передать дополнительные заголовки в третьем аргументе, и я уже пробовал это, но я сталкиваюсь с проблемой, что любой заголовок, который я передаю в него, и когда я вызываю это запрос 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 на вкладке «Консоль» на вкладке «Сеть», когда я пытаюсь увидеть заголовки запросов, когда применяются указанные выше случаи, он сообщает Временные заголовки отображаются - и я обнаружил некоторое переполнение стека ответы на этот вопрос, но ни один из них не решил мою проблему.

Последние 5-6 часов я потратил на поиск net решения, попробовал некоторые из моих идей, но все безрезультатно.


РЕДАКТИРОВАНИЕ: Эта проблема связана не с Angular, а с настройкой сервера и серверной части и обработкой предварительных запросов .

Ответы [ 2 ]

0 голосов
/ 24 марта 2020

После бессонной ночи я, наконец, решил проблему, и это была не проблема с Angular или моим кодом в нем, а скорее конфигурация веб-сервера.

Если кто-то испытывает что-то подобное, этот ответ может предоставить потенциальное решение, и это пример книги предварительного запроса :

Предварительный запрос CORS - это запрос CORS, который проверяет чтобы понять, понятен ли протокол CORS, и знает ли сервер, используя определенные методы и заголовки.

Это запрос OPTIONS, использующий три заголовка HTTP-запроса: Access-Control-Request-Method, Access-Control-Request -Headers и заголовок Origin.

Это означает, что перед каждым из запросов, сделанных приложением Angular, был еще один, который проверял бы параметры CORS на сервере через OPTIONS request.

Настройка моего сервера и 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 никогда не достигало этого условия, даже если я пытался с Почтальоном:

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

Причиной тому было то, что routes/web.php не обрабатывать любой запрос OPTIONS, так что он обрабатывался самим веб-сервером, и поскольку я использовал Apache с .htaccess, это также вызывало некоторые незначительные проблемы, но я не хотел придерживаться Apache справиться с этим. Я хотел придерживаться соглашения о том, что большинство вещей обрабатываются Lumen / Laravel.

Итак, чтобы мой CorsMiddleware перехватил запрос OPTIONS, я решена вся эта проблема с добавлением этого поверх моего основного routes/web.php файла:

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

Теперь упомянутый выше случай OPTIONS в CorsMiddleware, который никогда не случается, как это происходит, и это решено вся моя проблема.

0 голосов
/ 24 марта 2020

это может быть проблемой req = req.clone ({responseType: 'json'});

попытаться клонировать запрос в новый var, например: var newReq = req.clone ({responseType : 'json'});

.... остаток кода ... и затем возвратите новый запрос.

...