为什么要封装呢?
angular4自身提供的请求方法是用Observable来实现的。用的是观察者模式,个人认为这用来写请求是非常方便的。
一个项目里会有非常多的不同的请求,但是其实每个请求都会有些共性。比如:每个请求都要传Authorization,比如每个请求都要先判断后台返回的status字段为200时才是请求成功,后台正真返回的数据在data字段里,比如对于错误信息的处理都是一样的......等等。
所以我们需要封装出一个请求,去统一处理这些问题,从而保证组件里调用请求方法的时候收到的值都是可以直接拿来用的,几乎不用再写些重复的代码。
希望封装成什么样呢?
当然是越少重复的代码越好,我们就是想偷懒!!!!
怎么实现呢?
首先先新建一个请求的service,文件名为:request.service.ts。然后跟着我来虚拟需求,一步一步的慢慢来完善这个service。
需求A
1.请求方式为get。
2.默认的请求超时时间为3秒,可传入别的超时时间。
3.后台返回的成功的json为这样:
错误时这样:
实现A
request.service.ts
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{
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']; // <a href="https://www.jb51.cc/tag/cuowu/" target="_blank" class="keywords">错误</a>信息
throw new Error(err); // 抛出<a href="https://www.jb51.cc/tag/cuowu/" target="_blank" class="keywords">错误</a>
}
return res; // 返回最终值
}
}
需求B
1.为了安全,后台要求请求的头需要加上Authorization参数。
2.当请求失败(如404,500这种)时,处理好错误信息,最后的错误信息要像 实现A 里一样,是可以直接用的字符串类型的错误信息。
实现B
request.service.ts (只展示新增的代码,完整代码后面有)
export class RequestService {
/* 添加Authorization的属性 /
private addAuthorization(options:any):void {
options['headers'] = {
'Authorization': '1drf5dg4d7s4w7z',};
}
/** 获取数据
- param: url string 必填,time = this.setTimeout):Observable
{
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 */
/** <a href="https://www.jb51.cc/tag/houtai/" target="_blank" class="keywords">后台</a>有返回<a href="https://www.jb51.cc/tag/cuowu/" target="_blank" class="keywords">错误</a>信息时 */
if (data.hasOwnProperty('error') && data.hasOwnProperty('message')) { /* new */
res = data.message; /* new */
/** <a href="https://www.jb51.cc/tag/houtai/" target="_blank" class="keywords">后台</a>没有返回<a href="https://www.jb51.cc/tag/cuowu/" target="_blank" class="keywords">错误</a>信息只有<a href="https://www.jb51.cc/tag/cuowu/" target="_blank" class="keywords">错误</a>名时 */
} else if (data.hasOwnProperty('name')) { /* new */
let errName = data.name; /* new */
/** 请求超时 */
if (errName == 'TimeoutError') { /* new */
res = '对不起,请求超时了'; /* new */
}
/** <a href="https://www.jb51.cc/tag/houtai/" target="_blank" class="keywords">后台</a>返回未授权时 */
} else if (data == "Unauthorization") { /* new */
res = '您没有权限,请重新<a href="https://www.jb51.cc/tag/denglu/" target="_blank" class="keywords">登录</a>' /* new */
} else {
res = "哎呀,不知道是啥<a href="https://www.jb51.cc/tag/cuowu/" target="_blank" class="keywords">错误</a>~~"; /* new */
}
return Observable.throw(res); /* new */
}
}
完整的request service 代码
request.service.ts
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 必填,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']; // <a href="https://www.jb51.cc/tag/cuowu/" target="_blank" class="keywords">错误</a>信息
throw new Error(err); // 抛出<a href="https://www.jb51.cc/tag/cuowu/" target="_blank" class="keywords">错误</a>
}
return res; // 返回最终值
}
/** 对请求错误信息的处理
- param: err any 必填,需要处理的错误信息
- return: res string 处理后的结果
**/
public httpErrorFun(err:any):string {
let res:string = ''; // 处理后的结果
let data:any = err; // 需要处理的值
/** <a href="https://www.jb51.cc/tag/houtai/" target="_blank" class="keywords">后台</a>有返回<a href="https://www.jb51.cc/tag/cuowu/" target="_blank" class="keywords">错误</a>信息时 */
if (data.hasOwnProperty('error') && data.hasOwnProperty('message')) {
res = data.message;
/** <a href="https://www.jb51.cc/tag/houtai/" target="_blank" class="keywords">后台</a>没有返回<a href="https://www.jb51.cc/tag/cuowu/" target="_blank" class="keywords">错误</a>信息只有<a href="https://www.jb51.cc/tag/cuowu/" target="_blank" class="keywords">错误</a>名时 */
} else if (data.hasOwnProperty('name')) {
let errName = data.name;
/** 请求超时 */
if (errName == 'TimeoutError') {
res = '对不起,请求超时了';
}
/** <a href="https://www.jb51.cc/tag/houtai/" target="_blank" class="keywords">后台</a>返回未授权时 */
} else if (data == "Unauthorization") {
res = '您没有权限,请重新<a href="https://www.jb51.cc/tag/denglu/" target="_blank" class="keywords">登录</a>';
} else {
res = "哎呀,不知道是啥<a href="https://www.jb51.cc/tag/cuowu/" target="_blank" class="keywords">错误</a>~~";
}
return Observable.throw(res);
}
}
小结
至此,我们已经完成了一个满足基本需求的,可以公共使用的请求服务,接下来我们来看怎么在组件内调用。
调用
我们有个叫list的组件,要调用get请求,请求成功显示数据,请求失败,显示错误信息。
list.component.ts
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
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();
}
}