在REST风格中,每一个资源都只是对应一个网址,而一个代表资源的网址应该是一个名词,而不存在动词。比如:获取id为1的用户URL可以设计为:http://localhost:8080/user/1。
1.名称解释
REST Representational State Transfer
- Representational 资源:这可以是系统权限用户、角色和菜单,也可以是一些媒体类型的,如文本、图片、歌曲
- Transfer 表示层: 有了资源还需要确定如何表示这个资源。
- State 状态转换:现在中资源不是一成不变的,它是一个变化的过程,一个资源可以经历创建(create)、访问(visit)、修改(update)、和删除(delete)。
REST风格架构特点:
- 服务器存在一系列资源,每个资源通过单独唯一URI进行标识
- 客户端和服务器之间可以相互传递资源,而资源会以表现层得以展示
- 客户端通过HTTP协议所定义的运作对资源进行操作
2.HTTP动作
REST对应HTTP5种动作
- GET(VISIT):访问服务器资源
- POST(CREATE):提交服务器资源信息,用来创建新资源
- PUT(UPDATE):修改服务器资源信息。需要把所有属性一并提交
- PATCH(UPDATE):修改服务器已经存在的资源,可以部分资源属性提交。(有些Java类不能完全支持)
- DELETE:从服务器将资源删除
两个不常用的动作午觉 - HEAD:获取资源元数据(Content-type)
- OPTIONS:提供资源可以客户端修改的属性信息
URI设计:
GET /user/1
GET /users/{userName}/{note}
POST /user/{userName}/{note}
PUT /user/{id}/{userName}/{sex}/{note}
PATCH /user/{id}{userName}
3 使用Spring开发Rest风格
假设已经开发好了服务层(Service)和数据访问层(DAO),那么只需要开发控制器就可以了。对于DAO层而言,使用的是PO(Persistent Object),它直接对应数据库表。
POJO:
@Alias("user")
public class User {
private Long id ;
private String userName;
private SexEnum sexEnum = null;
private String note;
--- getter setter
}
对于PO,它的属性sex是一个枚举SexEnum类型,这会让前端难以理解。为了处理它,需要一个VO(View Object,视图对象)去转换
VO:
public class UserVo {
private Long id;
private String userName;
private int sexCode;
private String sexName;
private String note;
--- getter setter
}
简单使用案例:
@Controller
@RequestMapping
public class UserController {
@Autowired
private UserService userService = null;
// 转换 V0变为PO
private User changeToPo(UserVo userVo) {
User user = new User();
user.setId(userVo.getId());
user.setUserName(userVo.getUserName());
user.setSexEnum(SexEnum.getSexEnum(userVo.getSexCode()));
user.setNote(userVo.getNote());
return user;
}
// 转换PO变为VO
private UserVo changeToVo(User user) {
UserVo userVo = new UserVo();
userVo.setId(user.getId());
userVo.setUserName(user.getUserName());
userVo.setSexCode(user.getSexEnum().getCode());
userVo.setSexName(user.getSexEnum().getName());
userVo.setNote(user.getNote());
return userVo;
}
// PO列表转为VO列表
private List<UserVo> changeToVoes(List<User> poList){
List<UserVo> voList = new ArrayList<>();
for(User user: poList){
UserVo userVo = changeToVo(user);
voList.add(userVo);
}
return voList;
}
@GetMapping
@ResponseBody
public List<UserVo> findUsers(
@PathVariable("userName") String userName,
@PathVariable("note") String note,
@PathVariable("start") int start,
@PathVariable("limit") int limit){
List<User> userList = userService.findUsers(userName,note,start,limit);
return this.changeToVoes(userList);
}
4 使用 @RestController
现在前后端分离,使用JSON交互很普遍。如果第一个方式都加@ResponseBoby才能将数据转换为JSON,这有些冗余。Rest风格中,存在@RestController,相当于 @Controller+@ResponseBoby
@RestController
@RequestMapping
public class UserController { ...}
5 使用RestTemplate
获取用户:
public static UserVo getUser(Long id) {
RestTemplate restTemplate = new RestTemplate();
UserVo userVo = restTemplate.getForObject(
"http://localhost:/8080/user/{id}", UserVo.class, id
);
System.out.println(userVo.getUserName());
return userVo;
}
getForObject
- 第一个参数 URI 标明请求服务器什么资源,{id } 代表参数
- UserVo.class 表示请求将返回UserVo类的结果,服务器只会返回JSON类型的数据,RestTemplate内部会将其转变成Java对象
- 第二个参数后面,则是URI对应的参数,它是一个可变长的参数。
RestTemplate 多个参数的用法
public static List<UserVo> findUser(String userName,
String note, int start, int limit) {
RestTemplate restTemplate = new RestTemplate();
Map<String, Object> params = new HashMap<>();
params.put("userName", "user");
params.put("note", "note");
params.put("start", start);
params.put("limit", limit);
// Map 中的key和URI中的参数一一对应
String url = "http://localhost:/8080/users/{userName}/{note}/{start}/{limit}";
// 请求后端
ResponseEntity<List> responseEntity = restTemplate.getForEntity(url, List.class, params);
List<UserVo> userVos = responseEntity.getBody();
return userVos;
}
可以将参数用Map对象封装起来,Map的键和URI中所定义的参数是保持一致的。
新增用户的场景,新增用户时字段较多,往往会采用传递的请求体(Body)的方式 。
POST请求传递JSON请求体:
public static User insertUser(UserVo newUserVo) {
HttpHeaders headers = new HttpHeaders();
// 设置请求内容为JSON类型
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
// 创建请求实体对象
HttpEntity<UserVo> request = new HttpEntity<>(newUserVo, headers);
RestTemplate restTempl = new RestTemplate();
// 请求时传递请求实体的对象,并返回回填id的用户
User user = restTempl.postForObject("http://localhost:8080/user", request, User.class);
System.out.println(user.getId());
return user;
}