是时候再认识一下Angular中的`DI`了。依赖注入原文为:Dependency injection,简称为DI,这是一种重要的应用设计模式。后期在学习Spring Boot时还会广泛的应用这一特性。Angular神奇的实现了这一模式,这使得我们在项目中能够快速的适应多种场景。比如我们在单元测试中,某个组件在构造函数中注入了`Httpclient`,则在DI的作用下我们能够在不改变组件代码的前提下,引入`HttpModule`来使用真实的后台,引入`HttpTestingModule`来手动控制Http请求。
在Angular中,除了可注入教程已经演示过的`HttpClient`、`Router`、`ActivedRoute`以外,还可以注入其它任意的**服务**。
本节中,我们将创建一个发送当前用户已注销状态的**服务**,然后将该服务应用到**拦截器**及**组件**,最终查看这个**服务**是如何正常工作的。
## 上节答案
修正`ApiInterceptor`前先把注销的地址改成正确的81端口的地址:
```typescript
+++ b/first-app/src/app/nav/nav.component.ts
@@ -18,7 +18,7 @@ export class NavComponent implements OnInit {
}
onSubmit(): void {
- const url = 'http://angular.api.codedemo.club/teacher/logout';
+ const url = 'http://angular.api.codedemo.club:81/teacher/logout';
this.httpClient.get(url)
```
然后在拦截器增加处理以`http`打头的请求
```typescript
+++ b/first-app/src/app/api.interceptor.ts
@@ -19,6 +19,9 @@ export class ApiInterceptor implements HttpInterceptor {
if (url.startsWith('/')) {
// 如果以/打头,比如 /clazz,则直接拼接为:http://angular.api.codedemp.club/api/clazz
return this.api + url;
+ } else if (url.startsWith('http')) {
+ // 如果请求以http打头,比如http://angular.api.codedemo.club/teacher/logout,则什么也不做
+ return url;
} else {
// 如果不以/打头,比如clazz,则接拼的时候加入/
return this.api + '/' + url;
```
此时拦截器拦截到注销的请求地址`http://angular.api.codedemo.club:81/teacher/logout`后,则什么什么也不做。这样以来`ApiInterceptor`即可以为没有请求前缀的加入前缀,也支持历史上未改造轮子直接请求后台api地址。
## 当前问题
在继续学习之前,我们先模拟个使用情景。
- 打开浏览器,访问[http://localhost:4200/](http://localhost:4200/),使用用户名密码登录系统
- 保持半小时当前页面不动
- 半小时后再回到当前页
- 此时无论是刷新页面,还是点击注销。除不需要认证的教师管理模块外,需要认证的个人中心、班级管理、注销等功能全部失效
- 控制台报401认证失败错误
这是由于我们的后台出于安全的考虑做了一个设置:如果用户半个小时内没有与后台交互,则认为用户处于暂离状态,后台接着对处于暂离状态的用户做了注销处理。所以半小时以后,用户再次来到界面想进行一些需要认证的操作时,全部发生了401错误。
如果你没有耐心等待上半个小时,那么也可以如下操作:
- 打开浏览器,访问[http://localhost:4200/](http://localhost:4200/),使用用户名密码登录系统
- 打开控制台 ---- 网络选项卡
- 如果使用的是Chrome浏览器,则勾选`Preserve log`![image-20210409140835875](https://img.kancloud.cn/12/38/12385159447334cae680fa1cbab41e61_1580x288.png)
- Firefox浏览器,无需进行任何设置
- 点击注销按钮
- 使用用户名密码再次登录系统
- 来到控制台 ---- 网络,点击XHR,在搜索框中输入logout:
- ![image-20210409145428784](https://img.kancloud.cn/57/07/57071e7b56c6449a813450fe6a2fed83_1796x508.png)
- 然后在下面的记录上点右键 ---- Replay XHR
- ![image-20210409145502682](https://img.kancloud.cn/4c/85/4c8542e5ff2533aa2fea481f86f134ed_1396x372.png)
- 如果是Firefox浏览器,则选择Resend
- ![image-20210409145629045](https://img.kancloud.cn/7c/a5/7ca56eb0a89e8f9ceafbf0fef61d303f_2472x360.png)
经过上述一通操作,我们模拟了后台偷偷发生的注销操作,所以神奇的事情发生了:个人中心、班级管理全部因发生401错误而无法使用,同时注销按钮也一并失灵了,而且刷新当前页面也不起作用。
本节我们将共同解决访问题:当后台主动将用户注销时,前台在请求后台时发现发生了401错误,则将显示登录界面。
| 名称 | 链接 |
| -------- | ------------------------------------------------------------ |
| 本节源码 | [https://github.com/mengyunzhi/angular11-guild/archive/step6.7.zip](https://github.com/mengyunzhi/angular11-guild/archive/step6.7.zip) |
- 序言
- 第一章 Hello World
- 1.1 环境安装
- 1.2 Hello Angular
- 1.3 Hello World!
- 第二章 教师管理
- 2.1 教师列表
- 2.1.1 初始化原型
- 2.1.2 组件生命周期之初始化
- 2.1.3 ngFor
- 2.1.4 ngIf、ngTemplate
- 2.1.5 引用 Bootstrap
- 2.2 请求后台数据
- 2.2.1 HttpClient
- 2.2.2 请求数据
- 2.2.3 模块与依赖注入
- 2.2.4 异步与回调函数
- 2.2.5 集成测试
- 2.2.6 本章小节
- 2.3 新增教师
- 2.3.1 组件初始化
- 2.3.2 [(ngModel)]
- 2.3.3 对接后台
- 2.3.4 路由
- 2.4 编辑教师
- 2.4.1 组件初始化
- 2.4.2 获取路由参数
- 2.4.3 插值与模板表达式
- 2.4.4 初识泛型
- 2.4.5 更新教师
- 2.4.6 测试中的路由
- 2.5 删除教师
- 2.6 收尾工作
- 2.6.1 RouterLink
- 2.6.2 fontawesome图标库
- 2.6.3 firefox
- 2.7 总结
- 第三章 用户登录
- 3.1 初识单元测试
- 3.2 http概述
- 3.3 Basic access authentication
- 3.4 着陆组件
- 3.5 @Output
- 3.6 TypeScript 类
- 3.7 浏览器缓存
- 3.8 总结
- 第四章 个人中心
- 4.1 原型
- 4.2 管道
- 4.3 对接后台
- 4.4 x-auth-token认证
- 4.5 拦截器
- 4.6 小结
- 第五章 系统菜单
- 5.1 延迟及测试
- 5.2 手动创建组件
- 5.3 隐藏测试信息
- 5.4 规划路由
- 5.5 定义菜单
- 5.6 注销
- 5.7 小结
- 第六章 班级管理
- 6.1 新增班级
- 6.1.1 组件初始化
- 6.1.2 MockApi 新建班级
- 6.1.3 ApiInterceptor
- 6.1.4 数据验证
- 6.1.5 教师选择列表
- 6.1.6 MockApi 教师列表
- 6.1.7 代码重构
- 6.1.8 小结
- 6.2 教师列表组件
- 6.2.1 初始化
- 6.2.2 响应式表单
- 6.2.3 getTestScheduler()
- 6.2.4 应用组件
- 6.2.5 小结
- 6.3 班级列表
- 6.3.1 原型设计
- 6.3.2 初始化分页
- 6.3.3 MockApi
- 6.3.4 静态分页
- 6.3.5 动态分页
- 6.3.6 @Input()
- 6.4 编辑班级
- 6.4.1 测试模块
- 6.4.2 响应式表单验证
- 6.4.3 @Input()
- 6.4.4 FormGroup
- 6.4.5 自定义FormControl
- 6.4.6 代码重构
- 6.4.7 小结
- 6.5 删除班级
- 6.6 集成测试
- 6.6.1 惰性加载
- 6.6.2 API拦截器
- 6.6.3 路由与跳转
- 6.6.4 ngStyle
- 6.7 初识Service
- 6.7.1 catchError
- 6.7.2 单例服务
- 6.7.3 单元测试
- 6.8 小结
- 第七章 学生管理
- 7.1 班级列表组件
- 7.2 新增学生
- 7.2.1 exports
- 7.2.2 自定义验证器
- 7.2.3 异步验证器
- 7.2.4 再识DI
- 7.2.5 属性型指令
- 7.2.6 完成功能
- 7.2.7 小结
- 7.3 单元测试进阶
- 7.4 学生列表
- 7.4.1 JSON对象与对象
- 7.4.2 单元测试
- 7.4.3 分页模块
- 7.4.4 子组件测试
- 7.4.5 重构分页
- 7.5 删除学生
- 7.5.1 第三方dialog
- 7.5.2 批量删除
- 7.5.3 面向对象
- 7.6 集成测试
- 7.7 编辑学生
- 7.7.1 初始化
- 7.7.2 自定义provider
- 7.7.3 更新学生
- 7.7.4 集成测试
- 7.7.5 可订阅的路由参数
- 7.7.6 小结
- 7.8 总结
- 第八章 其它
- 8.1 打包构建
- 8.2 发布部署
- 第九章 总结