为什么要封装呢?
angular4 自身提供的请求方法是用 Observable 来实现的. 用的是观察者模式, 个人认为这用来写请求是非常方便的.
一个项目里会有非常多的不同的请求, 但是其实每个请求都会有些共性. 比如: 每个请求都要传 Authorization, 比如每个请求都要先判断后台返回的 status 字段为 200 时才是请求成功, 后台正真返回的数据在 data 字段里, 比如对于错误信息的处理都是一样的...... 等等.
所以我们需要封装出一个请求, 去统一处理这些问题, 从而保证组件里调用请求方法的时候收到的值都是可以直接拿来用的, 几乎不用再写些重复的代码.
希望封装成什么样呢?
当然是越少重复的代码越好, 我们就是想偷懒!!!!
怎么实现呢?
首先先新建一个请求的 service, 文件名为: request.service.ts. 然后跟着我来虚拟需求, 一步一步的慢慢来完善这个 service.
需求 A
1. 请求方式为 get.
2. 默认的请求超时时间为 3 秒, 可传入别的超时时间.
3. 后台返回的成功的 json 为这样:
- {
- "status": 200,
- "data" : ...
- }
错误时这样:
- {
- "status": 201,
- "msg" : "用户名或密码错误"
- }
实现 A
- request.service.ts
- /**
- ********************************************************************************************
- * @App: test
- * @author: isiico
- * @type: service
- * @src: services/request.service.ts
- *
- * @descriptions:
- * 请求的服务
- *
- ********************************************************************************************
- */
- // Angular Core
- import {Injectable} from '@angular/core';
- import {HttpClient} from '@angular/common/http';
- // rxjs
- import {Observable} from 'rxjs/Observable';
- import 'rxjs/add/operator/map';
- import 'rxjs/add/operator/timeout';
- import 'rxjs/add/observable/throw';
- @Injectable()
- export class RequestService {
- private setTimeout = 3000; // 默认的超时时间
- constructor(private http:HttpClient) {
- }
- /** 获取数据
- * param: url string 必填, 请求的 url
- * time number 可不填, 请求的超时时间, 如不填, 默认为 setTimeout
- * return: Observable HttpClient 的 get 请求, 请求完成后返回的值类型是 any
- **/
- public getData(url, time = this.setTimeout):Observable<any> {
- let thiUrl = url; // 用到的 url
- let thisTime = time; // 用到的超时时间
- return this.http.get(thiUrl)
- .timeout(thisTime)
- .map(res => this.resFun(res));
- }
- /** 返回数据的处理
- * param: data any 必填, 需要处理的数据
- * return: res any 返回处理后的值
- **/
- private resFun(data:any):any {
- let thisData:any = data; // 需要处理的值
- let res:any; // 最终值
- // 当 status 为 200 时
- if (thisData['status'] == 200) {
- res = thisData['data']; // 给最终值赋值
- } else {
- // 当 status 不为 200 时
- let err = thisData['msg']; // 错误信息
- throw new Error(err); // 抛出错误
- }
- return res; // 返回最终值
- }
- }
需求 B
1. 为了安全, 后台要求请求的头需要加上 Authorization 参数.
2. 当请求失败 (如 404,500 这种) 时, 处理好错误信息, 最后的错误信息要像 实现 A 里一样, 是可以直接用的字符串类型的错误信息.
实现 B
- request.service.ts (只展示新增的代码, 完整代码后面有)
- import 'rxjs/add/operator/catch';
- @Injectable()
- export class RequestService {
- /** 添加 Authorization 的属性 */
- private addAuthorization(options:any):void {
- options['headers'] = {
- 'Authorization': '1drf5dg4d7s4w7z',
- };
- }
- /** 获取数据
- * param: url string 必填, 请求的 url
- * time number 可不填, 请求的超时时间, 如不填, 默认为 setTimeout
- * return: Observable HttpClient 的 get 请求, 请求完成后返回的值类型是 any
- **/
- public getData(url, time = this.setTimeout):Observable<any> {
- let thiUrl = url; // 用到的 url
- let options = {}; // 请求的设置
- let thisTime = time; // 用到的超时时间
- this.addAuthorization(options); // 请求头里添加 Authorization 参数
- return this.http.get(thiUrl, options)
- .timeout(thisTime)
- .catch(this.httpErrorFun) // 处理错误信息(必须放在 timeout 和 map 之间)
- .map(res => this.resFun(res));
- }
- /** 对请求错误信息的处理
- * param: err any 必填, 需要处理的错误信息
- * return: res string 处理后的结果
- **/
- public httpErrorFun(err:any):string { /* new */
- let res:string = ''; // 处理后的结果 /* new */
- let data:any = err; // 需要处理的值 /* new */
- /** 后台有返回错误信息时 */
- if (data.hasOwnProperty('error') && data.hasOwnProperty('message')) { /* new */
- res = data.message; /* new */
- /** 后台没有返回错误信息只有错误名时 */
- } else if (data.hasOwnProperty('name')) { /* new */
- let errName = data.name; /* new */
- /** 请求超时 */
- if (errName == 'TimeoutError') { /* new */
- res = '对不起, 请求超时了'; /* new */
- }
- /** 后台返回未授权时 */
- } else if (data == "Unauthorization") { /* new */
- res = '您没有权限, 请重新登录' /* new */
- } else {
- res = "哎呀, 不知道是啥错误~~"; /* new */
- }
- return Observable.throw(res); /* new */
- }
- }
完整的 request service 代码
- request.service.ts
- /**
- ********************************************************************************************
- * @App: test
- * @author: isiico
- * @type: service
- * @src: services/request.service.ts
- *
- * @descriptions:
- * 请求的服务
- *
- ********************************************************************************************
- */
- // Angular Core
- import {Injectable} from '@angular/core';
- import {HttpClient} from '@angular/common/http';
- // rxjs
- import {Observable} from 'rxjs/Observable';
- import 'rxjs/add/operator/map';
- import 'rxjs/add/operator/timeout';
- import 'rxjs/add/observable/throw';
- import 'rxjs/add/operator/catch';
- @Injectable()
- export class RequestService {
- private setTimeout:number = 3000; // 默认的超时时间
- constructor(private http:HttpClient) {
- }
- /** 添加 Authorization 的属性 */
- private addAuthorization(options:any):void {
- options['headers'] = {
- 'Authorization': '1drf5dg4d7s4w7z',
- };
- }
- /** 获取数据
- * param: url string 必填, 请求的 url
- * time number 可不填, 请求的超时时间, 如不填, 默认为 setTimeout
- * return: Observable HttpClient 的 get 请求, 请求完成后返回的值类型是 any
- **/
- public getData(url, time = this.setTimeout):Observable<any> {
- let thiUrl = url; // 用到的 url
- let options = {}; // 请求的设置
- let thisTime = time; // 用到的超时时间
- this.addAuthorization(options); // 请求头里添加 Authorization 参数
- return this.http.get(thiUrl, options)
- .timeout(thisTime)
- .catch(this.httpErrorFun) // 处理错误信息(必须放在 timeout 和 map 之间)
- .map(res => this.resFun(res));
- }
- /** 返回数据的处理
- * param: data any 必填, 需要处理的数据
- * return: res any 返回处理后的值
- **/
- private resFun(data:any):any {
- let thisData:any = data; // 需要处理的值
- let res:any; // 最终值
- // 当 status 为 200 时
- if (thisData['status'] == 200) {
- res = thisData['data']; // 给最终值赋值
- } else {
- // 当 status 不为 200 时
- let err = thisData['msg']; // 错误信息
- throw new Error(err); // 抛出错误
- }
- return res; // 返回最终值
- }
- /** 对请求错误信息的处理
- * param: err any 必填, 需要处理的错误信息
- * return: res string 处理后的结果
- **/
- public httpErrorFun(err:any):string {
- let res:string = ''; // 处理后的结果
- let data:any = err; // 需要处理的值
- /** 后台有返回错误信息时 */
- if (data.hasOwnProperty('error') && data.hasOwnProperty('message')) {
- res = data.message;
- /** 后台没有返回错误信息只有错误名时 */
- } else if (data.hasOwnProperty('name')) {
- let errName = data.name;
- /** 请求超时 */
- if (errName == 'TimeoutError') {
- res = '对不起, 请求超时了';
- }
- /** 后台返回未授权时 */
- } else if (data == "Unauthorization") {
- res = '您没有权限, 请重新登录';
- } else {
- res = "哎呀, 不知道是啥错误~~";
- }
- return Observable.throw(res);
- }
- }
小结
至此, 我们已经完成了一个满足基本需求的, 可以公共使用的请求服务, 接下来我们来看怎么在组件内调用.
调用
我们有个叫 list 的组件, 要调用 get 请求, 请求成功显示数据, 请求失败, 显示错误信息.
- list.component.ts
- /**
- ********************************************************************************************
- * @App: test
- * @author: isiico
- * @type: component
- * @src: components/list.component.ts
- *
- * @descriptions:
- * list 组件
- *
- ********************************************************************************************
- */
- // Angular Core
- import { Component, OnInit } from '@angular/core';
- // Services
- import { RequestService } from "../services/request.service";
- @Component({
- moduleId: module.id,
- templateUrl: 'list.component.html'
- })
- export class ListComponent implements OnInit {
- listApi = '/assets/mock-data/list.json'; // 列表的 api 地址
- list:Array<any>; // 列表数据(类型为数组)
- listErrMsg: string = ''; // 列表请求的错误信息
- constructor(private req: RequestService) {
- }
- /** 获取 list */
- getList(){
- this.listErrMsg = ''; // 清空错误信息
- // 发送请求
- this.req.getData(this.cabinetListApi)
- .subscribe(
- res=>{
- // 请求成功
- this.cabinets = [];
- this.cabinets = res;
- },err=>{
- // 请求失败
- this.cabinets = [];
- this.listErrMsg = err;
- })
- }
- ngOnInit() {
- this.getList();
- }
- }
页面的显示自己去完成吧!
来源: http://www.jianshu.com/p/d211b21db45f