Http 错误处理不适用于 Angular 中的地图

Http Error Handling is not working with map in Angular

我是 Angular 的新手,我正在开发一个使用 Angular 5 作为前端和 Laravel5.5 作为后端的项目。

我想做什么: 我正在进行基于令牌的身份验证,以检查是否正确的用户正在向后端发送请求,如果令牌未通过身份验证,它将抛出异常,用户将被重定向到登录页面。

我做了什么: 我遵循了 Youtube 上的教程,该教程使用 Authguard 来验证用户,下面是我的代码。

auth.guard.ts

import {Injectable} from "@angular/core";
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from "@angular/router";
import {UserService} from "../_services/user.service";
import {Observable} from "rxjs/Rx";

@Injectable()
export class AuthGuard implements CanActivate {

constructor(private _router: Router, private _userService: UserService) {
}

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):  Observable<boolean> | boolean {
    let currentUser = JSON.parse(localStorage.getItem('currentUser'));
    console.log("AuthGuard.ts:  "+ currentUser);
    if (currentUser == null) {
        this._router.navigate(['/login'], {queryParams: {returnUrl: state.url}});
        return false;
    }
    else{
        let response = this._userService.verify().map(
            data => {
                console.log("This is the data returned in authguard: "+data)
                if (data !== null) {
                    // logged in so return true
                    // console.log("Not null");
                    return true;
                }
                // error when verify so redirect to login page with the return url
                this._router.navigate(['/login'], {queryParams: {returnUrl: state.url}});
                return false;
            },
            error => {
                console.log('myerror');
                // error when verify so redirect to login page with the return url
                this._router.navigate(['/login'], {queryParams: {returnUrl: state.url}});
                return false;
            });

        console.log("response : "+ typeof response);
        return response;
    }
}
}

在教程中他们使用 subscribe 而不是 map,但我做了一些研究,我发现我们不能将 subscribe 与 Observable 一起使用。

我的代码虽然显示订阅错误并将用户重定向到登录页面,但订阅它不会 return true 或 false 因此即使凭据正确,用户也不会重定向到下一页。

这里是验证函数的代码。

user.service.ts

import {Injectable} from "@angular/core";
import { HttpClient , HttpHeaders , HttpErrorResponse } from '@angular/common/http';
import {User} from "../_models/index";
import { Observable } from 'rxjs';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';

interface DataResponse { status: string; }

@Injectable()
export class UserService {

constructor(private http: HttpClient) {
}

verify(): Observable<any>  {
    let currentUser = JSON.parse(localStorage.getItem('currentUser'));
    let headers = new HttpHeaders({'Authorization': 'Bearer ' + currentUser.token});
    let abc = this.http.get<DataResponse>('http://127.0.0.1:8000/api/auth/verify', {headers:headers}).map(response =>{ 
                                                                                                        // console.log("hjsdgjd:  "+response.status ); 
                                                                                                            response
                                                                                                    })
                                                                                                    .catch(this.errorHandler);
    console.log("abc:  " + abc);
    return abc;
}
errorHandler(error: HttpErrorResponse){
    console.log('some thing');
    return Observable.throw(error.message);
}
}

这是我修改令牌时的console

我不知道你有没有按照教程去做,有没有自己做一些修改,但无论如何,你的代码在很多层面上都是错误的。

关于 subscribe,我不知道你从哪里找来的,发现你不能将它与 Observable 一起使用,因为这是你在使用 observables 时应该知道的第一个运算符。

让我重写你的代码并解释你做错了什么:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):  Observable<boolean> | boolean {

  let currentUser: any;

  // You need to surround your parsing with a try catch, in case it goes wrong
  try {
    currentUser = JSON.parse(localStorage.getItem('currentUser'));
  } catch { return false; }

  console.log("AuthGuard.ts:  " + currentUser);

  // Use falsy values, they cover more cases (such as empty or undefined)
  if(!currentUser) {
    this._router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
    return false;
  }
  else {
    // You will subscribe to your Observable, because you have to do something with the response
    this._userService.verify().subscribe(data => {

      console.log("This is the data returned in authguard: " + data)

      // This time, use truthy values (opposite of falsy)
      if (data) {
        // logged in so return true
        // console.log("Not null");
        return true;
      }
      // Empty response 
      this._router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
      return false;
    },
    error => {
      console.log('myerror');
      // error when verify so redirect to login page with the return url
      this._router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
      return false;
    });

  // Don' return anything here : this will be returned before your HTTP call ends, and return a falsy value ('return false;')
  // console.log("response : " + typeof response);
  // return response;
  }
}

我用拦截器解决了这个问题。

它的作用: 它从前端获取 http 请求,并在将请求发送到后端之前附加 header(包含令牌)。然后它从后端获取响应,检查响应是否有错误,如果有错误则将用户重定向到登录页面。

这是我的拦截器代码。

AuthorizationInterceptor.ts

import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { Router } from '@angular/router';

import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/throw';

@Injectable()
export class AuthorizationInterceptor implements HttpInterceptor
{
constructor(private _router: Router) { }

    private handleAuthError(err: HttpErrorResponse): Observable<any> {
        //handle your auth error or rethrow
        console.log('server error : ', err);
        if (err.status === 401 || err.status === 403) {
            //navigate /delete cookies or whatever
            localStorage.removeItem('currentUser');
            this._router.navigate(['/login']);
            // if you've caught / handled the error, you don't want to rethrow it unless you also want downstream consumers to have to handle it as well.
            // return Observable.of(err.message);
        }
        return Observable.throw(err);
    }

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>
{
    // console.log('resquest passing from interceptor....', req, next);
    var authReq;

    let currentUser = JSON.parse(localStorage.getItem('currentUser'));
    // console.log("In the Interceptor: " + req);
    if (currentUser) {

        // Clone the request to add the new header.
        req = req.clone({headers: req.headers.set('Authorization', 'Bearer ' + currentUser.token)});
        // console.log("Authentication Request:  " + authReq);
        // shortcut for above...
        // const authReq = req.clone({setHeaders: {Authorization: authHeader}});

        // Pass on the cloned request instead of the original request.
    }

 return next.handle(req).catch(x => this.handleAuthError(x));
}
}

注意:不要忘记在 module.ts 文件中导入拦截器。 请参阅 This 构建拦截器指南。