上一节介绍了friend模块,本节介绍了friend模块中控件相关的三个服务程序。使用HttpClient拦截器发送用户认证信息在进入好友模块之前,需要向服务器发送认证信息,这里使用angularHttpClient拦截器发送。官方对拦截器的解释是:HTTP拦截机制是@angular/common/http中的主要特性之一。使用这种拦截机制,您可以声明拦截器来监视和转换从应用程序发送到服务器的HTTP请求。拦截器还可用于监视和转换从服务器返回到应用程序的那些响应。多个选择器形成一个“请求/响应处理程序”的双向链表。如果想了解更多关于拦截器的知识,可以阅读官方文档。每次向服务器请求好友列表时,我们都使用拦截器将身份验证信息添加到标头中。具体代码如下:import{Injectable}from'@angular/core';从'@angular/common/http'导入{HttpEvent、HttpInterceptor、HttpHandler、HttpRequest};从'rxjs/observable'导入{Observable};import{AuthTokenService}from'./authtoken.service';@Injectable()exportclassAuthInterceptorimplementsHttpInterceptor{constructor(privatetokenServ:AuthTokenService){}intercept(req:HttpRequest,next:HttpHandler):Observable>{//获取认证信息constauth=this.tokenServ.getToken();//克隆请求,添加新的头部信息constauthReq=req.clone({headers:req.headers.set('Authorization','Bearer'+auth)});返回next.handle(authReq);}}要实现一个拦截器,需要实现一个类(AuthInterceptor),该类实现了HttpInterceptor接口中的intercept()方法。在intercept()方法中,首先通过AuthTokenService的getToken()方法获取认证信息。这些认证信息是登录或注册成功后服务器发回的jwt认证信息。服务器如何发送这些信息,请参考第三节。认证信息的内容为登录或认证用户ID。因为HttpRequest实例的属性是只读的(readonly),要修改请求信息只能先克隆。这里使用clone()方法在请求的头部信息中加入认证信息(clone()方法的hash参数允许你在复制clone的同时改变请求的一些特定属性)。最后调用next.handle(),让请求流进入下一个拦截器,最终传递给后端处理器。最后,您需要将此拦截器提供给模块。AuthInterceptor拦截器是由Angular依赖注入(DI)系统管理的服务。您必须在提供HttpClient(或其所有级别的父注入器)设备的同一注入器中提供这些拦截器。添加{provide:HTTP_INTERCEPTORS,useClass:AuthInterceptor,multi:true}到friend模块的提供者。现在在好友模块中发送到服务器的每个请求都会将身份验证信息添加到标头中。补充内容:服务端jwt认证中间件express的jwt中间件定义代码如下:varexpressJwt=require('express-jwt');//使用jwt拦截app.use(expressJwt({secret:'secret'}));//处理jwt异常app.use(function(err,req,res,next){if(err.name==='UnauthorizedError'){res.status(401).send({'code':401,'msg':'无效令牌'});}});app.use('/friends',friends);一定要把处理好友访问的路由放在jwt中间件后面,否则无法验证jwt。使用路由守卫保证非登录用户无法访问好友信息。上一节介绍路由时,在路由配置中添加了canActivate:[AuthGuardService]。这是角度路由守卫服务。路由守卫的作用在官方文档中是这样解释的:现在任何用户都可以随时随地导航。但有时这是不对的。用户可能没有导航到目标组件的权限。可能用户必须先登录(验证)。在显示目标组件之前,您可能需要获取一些数据。您可能希望在离开组件之前保存您的更改。您可能想问用户:是否要放弃此更改而不保存它们?您可以在路由配置中添加守卫来处理这些情况。守卫返回一个值来控制路由器的行为:如果它返回真,导航过程继续;如果它返回false,则导航过程终止并且用户保持在原处。这里我们使用路由守卫来要求用户在导航到生日模块中的控件之前先登录。代码如下:import{Injectable}from'@angular/core';从'@angular/router'导入{CanActivate,ActivatedRouteSnapshot,RouterStateSnapshot,Router};从'./user.service'导入{UserService};import{AuthTokenService}from'./authtoken.service';@Injectable()exportclassAuthGuardServiceimplementsCanActivate{constructor(privatetokenServe:AuthTokenService,privaterouter:Router){}canActivate(route:ActivatedRouteSnapshot,state:RouterStateSnapshot){if(this.tokenServegetToken()!==null){返回真;}this.router.navigate(['/login']);返回假;}}路由守卫类也是一个注入服务类,需要实现CanActivate接口方法的canActivate()。canActivate()方法实现了保护代码。代码很简单,从AuthTokenService类的getToken()中获取认证信息的值,有则返回true,没有则导航到登录页面。并返回假。最后,记得在生日模块中将AuthGuardService添加到providers中。birthday.service数据提供服务介绍BirthdayService类为生日模块提供数据服务,代码如下:import{Injectable}from'@angular/core';从'@angular/common/http'导入{HttpClient,HttpHeaders,HttpErrorResponse};import{UserService}from'../user.service';import'rxjs/add/operator/map';exportclassFriend{constructor(publicfid:number,publicfname:string,publicfbirth:Date,publicfnumber:string,publicfemail:string,publicfgroup:string,publicstate:string,publicphoto:string,publicuid:number){}}@Injectable()exportclassBirthdayService{constructor(privateuserServ:UserService,privatehttp:HttpClient){}//获取所有好友信息getFriends(){returnthis.http.get('http://localhost:3000/friends/friend-list',{observe:'response'});}//获取单个好友信息getFriend(id:number|string){返回这个。得到朋友()。地图(资源=>{我f(res.body['code']==='200'){returnres.body['results'].find(result=>result.fid===+id);}});}//修改好友信息editFriend(friend:Friend){constbody={'value':friend,'operate':'edit'};returnthis.http.post('http://localhost:3000/friends/editfriend',body);}//新好友信息newFriend(friend:Friend){constbody={'value':friend,'operate':'new'};returnthis.http.post('http://localhost:3000/friends/editfriend',body);}//删除好友deleteFriend(friend:Friend){constbody={'value':friend,'operate':'delete'};returnthis.http.post('http://localhost:3000/friends/editfriend',body);}//错误处理handleError(err:HttpErrorResponse):string{if(err.errorinstanceofError){return'发生错误,错误信息:'+err.error.message;}else{console.log(`后端返回代码${err.status},bodywas:${err.error['msg']}`);返回呃r.error['msg'];}}}首先在类外定义一个Friend类,在这个类中定义好友信息。BirthdayService类的主要功能有6个部分:获取所有好友信息。通过HttpClient的get方法发送请求获取所有好友信息。获取个别好友信息。getFriends()方法返回一个Observable对象,使用Observable的map()函数的回调找到id对应的单个好友对象,并继续发射Observable对象。修改好友信息。将修改后的好友信息发布到服务器。在发送的body中,除了修改后的好友对象外,还发送了一个字符串属性:'operate':'edit',用于区分是修改好友还是新建好友。这里的edit代表修改信息。(具体服务器操作代码将在下一章介绍)。创建新的朋友信息。与修改好友信息一样,只是将body中的'operate'改为'new'。删好友。也和修改好友信息一样,只是把正文中的‘操作’改成了‘删除’。错误处理。如果客户端(angular代码)出错,将抛出Error类型的异常。如果错误类型为Error类型,则表示前端有错误,会返回错误信息:'发生错误,错误信息:'+err.error.message;。如果后台有错误,打印出错误状态和信息。生日模块的服务程序介绍到此结束。下一章将描述服务器端express框架如何处理这些请求。我今天把我的代码上传到了github,供大家参考。地址如下:前端:https://github.com/db991400/b...后端:https://github.com/db991400/b...