Angular 框架中内置了通过 HTTP/HTTPS 访问后端服务的模块 HttpClientModule, 通过模块中的 HttpClient 对象,开发者可以轻松的实现访问后端的 restful 接口。因为 HttpClient 采用了响应式的编程风格(rxjs),所以很多初学者对于如何做错误处理会比较模糊,本文试图通过一些例子给读者一些指引。
subscribe 方法中的错误处理
我们知道, 在调用 HttpClient 的网络访问方法(比如: GET, POST, PUT 等)时会返回一个 Observable 的对象,通过调用改对象上的 subscribe 方法可以拿到返回的结果。如在课堂案例中,访问后端登录接口的代码如下:
login.component.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| login() { console.log('login, userId = ' + this.userId + ', pwd = ' + this.pwd); let ob = this.securitySrv.login(this.userId, this.pwd);
ob.subscribe(res => { if (res['succ']) { console.log('success'); this.securitySrv.token = res['data']; this.router.navigate(["home"]); } else { console.log('fail'); } }); }
|
security-srv.service.ts
1 2 3
| login(username: string, password: string) { return this.http.post(`#123;this.BASE_URL}/api/login`, { userId: username, password: password }); }
|
以上的组件代码(login.component.ts)的第3行和第5行,是为了突出在组件调用中生成了 Observable 对象才分开写的,通常的写法时连在一起,如下:
1 2 3 4 5 6 7 8 9 10 11 12
| login() { console.log('login, userId = ' + this.userId + ', pwd = ' + this.pwd); let ob = this.securitySrv.login(this.userId, this.pwd).subscribe(res => { if (res['succ']) { console.log('success'); this.securitySrv.token = res['data']; this.router.navigate(["home"]); } else { console.log('fail'); } }); }
|
注意:对于业务可识别的错误,我们一般采用判断返回对象中 succ 属性的值来判断,如以上代码中的第4行到第10行所展示的
为了处理技术性错误,比如网络不能访问,跨域错误等等,我们可以采用 subscribe 内置的错误处理来完成。修改 login.component.ts 中的 login 方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| logi() { console.log('login, userId = ' + this.userId + ', pwd = ' + this.pwd);
let ob = this.securitySrv.login(this.userId, this.pwd);
ob.subscribe(res => { if (res["succ"]) { this.securitySrv.token = res["data"]; this.router.navigate(["home"]); } else { console.log('fail'); } }, error => { console.log(error) alert('网络错误') }) }
|
可以看到,我们调用 subscribe 方法时,多加了一个参数,该参数是一个箭头函数,参数为调用时的错误信息对象 error。然后就可以在箭头函数的代码段完成处理处理,比如提示用户,记录错误信息等。
对错误进行分类处理
如果我们希望得到错误的详细信息或是按错误的类别进行分类处理,那又要怎么做呢?
实际上 error 对象是 HttpErrorResponse 的实例,包含了错误的信息信息。
1
| HttpErrorResponse {headers: HttpHeaders, status: 0, statusText: "Unknown Error", url: "https://uat-1.hohistar.com.cn1/api/login", ok: false, …}
|
这样我们就可以根据 HttpErrorResponse 中的相关信息,进行分类处理。 示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| login() { console.log('login, userId = ' + this.userId + ', pwd = ' + this.pwd);
let ob = this.securitySrv.login(this.userId, this.pwd);
ob.subscribe(res => { if (res["succ"]) { this.securitySrv.token = res["data"]; this.router.navigate(["home"]); } else { console.log('fail'); } }, error => { console.log(error) if (error.error instanceof ErrorEvent) { console.error('错误: ', error.error.message); } else { console.error(`执行业务方法出错: #123;error.status} - #123;error.error}`) } }) } }
|
在Service中处理错误
在实际项目中建议将错误处理放在 Service 中来完成,在界面上 (Component) 中应该只需要简单的显示错误即可, 在 HttpClient 的响应式编程中我们也可以非常轻松过的实现。 在 Service 中定义一个错误处理的私有方法,在该方法中对 error 进行分类处理,如果需要提示用户,则使用 showError 将信息包装为 error 抛出给 Component。然后通过 rxjs 中的 pipe, 将该处理函数加入到整个处理中。 改造后的 Service 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| private handlerError(error: HttpErrorResponse) { console.log('service handle error: ' + error); return throwError('服务器无法访问'); }
login(username: string, password: string) { return this.http.post(`#123;this.BASE_URL}/api/login`, { userId: username, password: password }) .pipe( catchError(this.handlerError) ); }
|
因为在 Service 进行了错误进行了处理,所以 Component 中只需要简单的截获显示错误信息即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| login() { console.log('login, userId = ' + this.userId + ', pwd = ' + this.pwd);
let ob = this.securitySrv.login(this.userId, this.pwd);
ob.subscribe((res: ApiResult<string>) => { if (res["succ"]) { this.securitySrv.token = res["data"]; this.router.navigate(["home"]); } else { console.log('fail'); } }, error => { alert(error) }) }
|
注意最后的箭头函数, 在这里截获的错误(error)是已经经过转换的error, 包含了可以显示给用户的信息,所以直接显示即可