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 构建拦截器指南。
我是 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 构建拦截器指南。