import { Injectable } from '@angular/core'; import { HttpClient,HttpHeaders } from "@angular/common/http"; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import 'rxjs/add/observable/forkJoin'; @Injectable() export class ServiceService { movies:string; httpOptions:Object; constructor(public http:HttpClient) { this.movies = "/movies"; this.httpOptions = { headers:new HttpHeaders({ 'Content-Type':'application/x-www-form-urlencoded;charset=UTF-8', }), } } /**登录模块开始*/ loginMovies(body){ const url = this.movies+"/person/exsit"; const param = 'userName='+body.userName+"&passWord="+body.password; return this.http.post(url,param,this.httpOptions); } /**登录模块结束*/ //首页; getPersonItem(param){ const url = this.movies+"/person/item"; return this.http.get(url,{params:param}); } //个人中心 getPersonList(){ const url = this.movies+"/person/list"; return this.http.get(url); /**首页模块结束 */ }
上述有三个请求与后端personController类中三个接口方法一一对应,这里面的请求方式官网有,这里不做赘述,this.httpOptions是设置请求头。然后再app.modules.ts中添加到provides,所谓的依赖注入,这样就可以在各个页面调用servcie方法了
providers: [ServiceService,httpInterceptorProviders]
httpInterceptorProviders 是前端拦截器,前端每次请求结果都会出现成功或者错误,所以在拦截器中统一处理返回结果使代码更简洁。
2、前端拦截器的实现
在app文件在新建InterceptorService.ts文件,代码如下:
import { Injectable } from '@angular/core'; import { HttpEvent,HttpInterceptor,HttpHandler,HttpRequest,HttpResponse} from "@angular/common/http"; import {Observable} from "rxjs/Observable"; import { ErrorObservable } from 'rxjs/observable/ErrorObservable'; import { mergeMap } from 'rxjs/operators'; import {Router} from '@angular/router'; @Injectable() export class InterceptorService implements HttpInterceptor{ constructor( private router:Router, ){ } authorization:string = ""; authReq:any; intercept(req:HttpRequest<any>,next:HttpHandler):Observable<HttpEvent<any>>{ this.authorization = "mso " + localStorage.getItem("accessToken"); if (req.url.indexOf('/person/exsit') === -1) { this.authReq = req.clone({ url:req.url, headers:req.headers.set("Authorization",this.authorization) }); }else{ this.authReq = req.clone({ url:req.url, }); } return next.handle(this.authReq).pipe(mergeMap((event:any) => { if(event instanceof HttpResponse && event.body === null){ return this.handleData(event); } return Observable.create(observer => observer.next(event)); })); } private handleData(event: HttpResponse<any>): Observable<any> { // 业务处理:一些通用操作 switch (event.status) { case 200: if (event instanceof HttpResponse) { const body: any = event.body; if (body === null) { this.backForLoginOut(); } } break; case 401: // 未登录状态码 this.backForLoginOut(); break; case 404: case 500: break; default: return ErrorObservable.create(event); } } private backForLoginOut(){ if(localStorage.getItem("accessToken") !== null || localStorage.getItem("person")!== null){ localStorage.removeItem("accessToken"); localStorage.removeItem("person"); } if(localStorage.getItem("accessToken") === null && localStorage.getItem("person") === null){ this.router.navigateByUrl('/login'); } } }
拦截器的实现官网也详细说明了,但是拦截器有几大坑:
a、如果用的是angular2,你请求是采用的是import { Http } from "@angular/http"包http,那么拦截器无效,你可能需要另一种写法了,angular4、5、6都是采用import { HttpClient,HttpHeaders } from "@angular/common/http"包下HttpClient和请求头HttpHeaders ;
b、拦截器返回结果的方法中:
return next.handle(this.authReq).pipe(mergeMap((event:any) => { if(event instanceof HttpResponse && event.body === null){ return this.handleData(event); } return Observable.create(observer => observer.next(event)); }));
打断点查看这个方法一次请求会循环两次,第一次event:{type:0} ,第二次才会返回对象,截图如下:
第一次
第二次
但是如果以我上述后端拦截器token无效的情况处理代码(就是我注释的那段代码,我注释的代码重点的作用是返回401,可以回看),这个逻辑只循环一次,所以我将后端代码返回token无效的代码注释,前端拦截器在后端代码注释的情况下第二次返回的event结果体存在event.body=== null ,以这个条件进行token是否有效判断;