在`Spring`中路由不叫路由,而叫做:`Request Mapping 请求映射`。路由(route)就是数据转发,而路由在按自己的规则进行数发的,而这个规则在英文中习惯的称为`map`,我们译过来叫做`映射`。所以`angular`中的`路由`与`spring`在此的`请求映射`只是说法不同而已,解决的都是数据依照什么规则进行转发的问题。
>[info] Thinking: 使用路径参数并获取参数中的路由;Coding: Angular及Spring中如写组织代码来实现该功能。
# 定义参数路由
在路由定义时,`spring`首先找到拉于控制器上的`@RequestMapping`注解,并获取注解中的值;然后以此值为基础,再找位于方法中的相关注解,比如:`@GetMapping`、`@PostMapping`等。
我们在前面的小节中,已经使用了如下代码来定义获取所有教师列表的路由地址:
```java
@RequestMapping("Teacher") ①
public class TeacherController {
...
@GetMapping ②
@CrossOrigin("*")
public List<Teacher> getAll() {
```
按照上述原则,最终得到的路由值为:`Teacher + ''` = `Teacher`。
# 获取路径变量的值
注解和有些语言的函数一样,是可以设置默认值的,比如如果我们使用`@GetMapping`注解。则表示:全部使用默认值设置该注解的值。当我们使用`@GetMapping("{id}")`表示:使用`"{id}"`来设置`@GetMapping`的默认属性`name`。`@GetMapping`除默认属性`name`外,还存在诸如`value`、`path`、`params`等属性。
在`spring`中通过设置`@GetMapping`注解的默认属性来设置路径变量,使用`@PathVariable`注解来获取绑定后的值。
TeacherController
```java
@GetMapping("{id}") ➊
public Teacher getById(@PathVariable Long id) { ➋
logger.info(id.toString());
return null;
}
```
* ➊ 声明映射路径变量值为`id`,当请求地址为`Teacher/1`时,符合本条规则。
* ➋ 获取绑定的`id`值。
## 测试
启用系统并使用`REST Client`进行测试。
![](https://img.kancloud.cn/92/a3/92a3015e0c3fc681051743cfae91b71b_994x279.png)
打开系统控制台,查看生成的日志:
![](https://img.kancloud.cn/d8/f9/d8f95d5a9ea6b9d40910908be5cfa08a_1635x436.png)
成功的打印了`id`的值。
## 使用SQL语句获取
我们将前面学习过的获取多条教师数据的代码稍做修改:
```java
/**
* 根据ID获取数据表中的教师数据并返回,用于查询某个教师的数据
* 虽然在学习的过程中,我们将方法中的每条语句都加入注释会有利于我们的理解。
* 但在生产的环境中,我们并不推荐在方法体中加入注释。
* 我们认为:
* 1 每个方法都应该是足够短小的。
* 2 每个方法的注释都是可以在方法头部说明的。
* 3 在代码输写时,我们更注重的是业务逻辑层面的交流而非coding方法的交流。
* 如果我们认为方法中的代码的确是需要注释的(比如一些新的方法、新的思想的引入,我们想其它的成员能够快速的学习到该技巧)
* 那么应该该代码段抽离出来,变成一个新的方法,然后在该方法上加入注释。
* @param id 教师ID
* @return
*/
@GetMapping("{id}")
@CrossOrigin("*") ①
public Teacher getById(@PathVariable Long id) {
Teacher teacher = new Teacher(); ➊
RowCallbackHandler rowCallbackHandler = new RowCallbackHandler() { ②
@Override
public void processRow(ResultSet resultSet) throws SQLException {
teacher.setId(resultSet.getLong("id"));
teacher.setName(resultSet.getString("name"));
teacher.setSex(resultSet.getBoolean("sex"));
teacher.setUsername(resultSet.getString("username"));
teacher.setEmail(resultSet.getString("email"));
teacher.setCreateTime(resultSet.getLong("create_time"));
teacher.setUpdateTime(resultSet.getLong("update_time"));
}
};
String query = String.format("select id, name, sex, username, email, create_time, update_time from teacher where id = %d", id); ➋
jdbcTemplate.query(query, rowCallbackHandler); ③
return teacher; ➌
}
```
* ➊ 初始化返回数据。
* ➋ 撰写查询语句。`String.format()`为字符串格式化函数,该代码中使用了`id`的值来替换了字符串中的`%d`。
* ➌ 返回数据。
> 由于获取到的数据要么有0条要么有1条,所以该赋值语句要么不执行,如果执行那么只会执行1次。
## 测试
在程序的编写中我们需要尽量规避一些人为的不稳定地、无法快速重现或是难以共享的因素。而`REST Client`由于是人为地在客户端内进行输入点击,所以它是我们在开发中进行测试选择的下策。 在`IDEA`中除了`REST Client`以外我们还可以选择另外一个相对较抽象但却更符合我们习惯的工作,这就是`HTTP Request`。在`IDEA`中可以轻松的将`REST Client`转换为`HTTP Request`。
![](https://img.kancloud.cn/9f/a8/9fa81cce0d97f03ecde76a631c050c19_1671x163.png)
点击红色的按钮便可以发起`http`请求协助我们的完成测试工作。更重要的是该请求是以文件形式存在的,我们可以很方便地重新运行测试或是将自己的测试文件与伙伴们进行分享,或是共同完成某个测试。
由于启用了`Http Request`,所以本次测试我直接把测试的代码给大家即可:
```
GET http://localhost:8080/Teacher/1 ➊
```
* ➊ 请求方法为`GET`,请求地址为: `http://localhost:8080/Teacher/1`
重新启动应用并运行测试代码,得到的结果如下:
```
GET http://localhost:8080/Teacher/1
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 24 Oct 2019 06:18:39 GMT
{
"id": 1,
"name": "张三",
"sex": true,
"username": "zhangsan",
"email": "zhangsan@mail.com",
"createTime": 1569721598000,
"updateTime": 1569721598000
}
Response code: 200; Time: 31ms; Content length: 135 bytes
```
在返回的结果中,我们看到了想要的教师信息,测试成功。
# 本节作业
将在新增教师测试中使用`REST Client`转换为`HTTP Request`测试方法,查看转换后的代码并尝试找出使用`HTTP Request`测试的规律。
# 参考文档
| 名称 | 链接 | 预计学习时长(分) |
| --- | --- | --- |
| 使用JDBC访问数据库 | [https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/data-access.html#jdbc-core](https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/data-access.html#jdbc-core) | 15 |
| 使用Spring创建REST服务 | [https://spring.io/guides/tutorials/bookmarks/](https://spring.io/guides/tutorials/bookmarks/) | 15 |
| 请求映射 | [https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/web.html#mvc-ann-requestmapping](https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/web.html#mvc-ann-requestmapping) | 15 |
| HTTP Request | [https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html](https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html) | 15 |
| 源码地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step2.4.2](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step2.4.2) | - |
- 序言
- 第一章:Hello World
- 第一节:Angular准备工作
- 1 Node.js
- 2 npm
- 3 WebStorm
- 第二节:Hello Angular
- 第三节:Spring Boot准备工作
- 1 JDK
- 2 MAVEN
- 3 IDEA
- 第四节:Hello Spring Boot
- 1 Spring Initializr
- 2 Hello Spring Boot!
- 3 maven国内源配置
- 4 package与import
- 第五节:Hello Spring Boot + Angular
- 1 依赖注入【前】
- 2 HttpClient获取数据【前】
- 3 数据绑定【前】
- 4 回调函数【选学】
- 第二章 教师管理
- 第一节 数据库初始化
- 第二节 CRUD之R查数据
- 1 原型初始化【前】
- 2 连接数据库【后】
- 3 使用JDBC读取数据【后】
- 4 前后台对接
- 5 ng-if【前】
- 6 日期管道【前】
- 第三节 CRUD之C增数据
- 1 新建组件并映射路由【前】
- 2 模板驱动表单【前】
- 3 httpClient post请求【前】
- 4 保存数据【后】
- 5 组件间调用【前】
- 第四节 CRUD之U改数据
- 1 路由参数【前】
- 2 请求映射【后】
- 3 前后台对接【前】
- 4 更新数据【前】
- 5 更新某个教师【后】
- 6 路由器链接【前】
- 7 观察者模式【前】
- 第五节 CRUD之D删数据
- 1 绑定到用户输入事件【前】
- 2 删除某个教师【后】
- 第六节 代码重构
- 1 文件夹化【前】
- 2 优化交互体验【前】
- 3 相对与绝对地址【前】
- 第三章 班级管理
- 第一节 JPA初始化数据表
- 第二节 班级列表
- 1 新建模块【前】
- 2 初识单元测试【前】
- 3 初始化原型【前】
- 4 面向对象【前】
- 5 测试HTTP请求【前】
- 6 测试INPUT【前】
- 7 测试BUTTON【前】
- 8 @RequestParam【后】
- 9 Repository【后】
- 10 前后台对接【前】
- 第三节 新增班级
- 1 初始化【前】
- 2 响应式表单【前】
- 3 测试POST请求【前】
- 4 JPA插入数据【后】
- 5 单元测试【后】
- 6 惰性加载【前】
- 7 对接【前】
- 第四节 编辑班级
- 1 FormGroup【前】
- 2 x、[x]、{{x}}与(x)【前】
- 3 模拟路由服务【前】
- 4 测试间谍spy【前】
- 5 使用JPA更新数据【后】
- 6 分层开发【后】
- 7 前后台对接
- 8 深入imports【前】
- 9 深入exports【前】
- 第五节 选择教师组件
- 1 初始化【前】
- 2 动态数据绑定【前】
- 3 初识泛型
- 4 @Output()【前】
- 5 @Input()【前】
- 6 再识单元测试【前】
- 7 其它问题
- 第六节 删除班级
- 1 TDD【前】
- 2 TDD【后】
- 3 前后台对接
- 第四章 学生管理
- 第一节 引入Bootstrap【前】
- 第二节 NAV导航组件【前】
- 1 初始化
- 2 Bootstrap格式化
- 3 RouterLinkActive
- 第三节 footer组件【前】
- 第四节 欢迎界面【前】
- 第五节 新增学生
- 1 初始化【前】
- 2 选择班级组件【前】
- 3 复用选择组件【前】
- 4 完善功能【前】
- 5 MVC【前】
- 6 非NULL校验【后】
- 7 唯一性校验【后】
- 8 @PrePersist【后】
- 9 CM层开发【后】
- 10 集成测试
- 第六节 学生列表
- 1 分页【后】
- 2 HashMap与LinkedHashMap
- 3 初识综合查询【后】
- 4 综合查询进阶【后】
- 5 小试综合查询【后】
- 6 初始化【前】
- 7 M层【前】
- 8 单元测试与分页【前】
- 9 单选与多选【前】
- 10 集成测试
- 第七节 编辑学生
- 1 初始化【前】
- 2 嵌套组件测试【前】
- 3 功能开发【前】
- 4 JsonPath【后】
- 5 spyOn【后】
- 6 集成测试
- 7 @Input 异步传值【前】
- 8 值传递与引入传递
- 9 @PreUpdate【后】
- 10 表单验证【前】
- 第八节 删除学生
- 1 CSS选择器【前】
- 2 confirm【前】
- 3 功能开发与测试【后】
- 4 集成测试
- 5 定制提示框【前】
- 6 引入图标库【前】
- 第九节 集成测试
- 第五章 登录与注销
- 第一节:普通登录
- 1 原型【前】
- 2 功能设计【前】
- 3 功能设计【后】
- 4 应用登录组件【前】
- 5 注销【前】
- 6 保留登录状态【前】
- 第二节:你是谁
- 1 过滤器【后】
- 2 令牌机制【后】
- 3 装饰器模式【后】
- 4 拦截器【前】
- 5 RxJS操作符【前】
- 6 用户登录与注销【后】
- 7 个人中心【前】
- 8 拦截器【后】
- 9 集成测试
- 10 单例模式
- 第六章 课程管理
- 第一节 新增课程
- 1 初始化【前】
- 2 嵌套组件测试【前】
- 3 async管道【前】
- 4 优雅的测试【前】
- 5 功能开发【前】
- 6 实体监听器【后】
- 7 @ManyToMany【后】
- 8 集成测试【前】
- 9 异步验证器【前】
- 10 详解CORS【前】
- 第二节 课程列表
- 第三节 果断
- 1 初始化【前】
- 2 分页组件【前】
- 2 分页组件【前】
- 3 综合查询【前】
- 4 综合查询【后】
- 4 综合查询【后】
- 第节 班级列表
- 第节 教师列表
- 第节 编辑课程
- TODO返回机制【前】
- 4 弹出框组件【前】
- 5 多路由出口【前】
- 第节 删除课程
- 第七章 权限管理
- 第一节 AOP
- 总结
- 开发规范
- 备用