Lingmoumou's Blog

きっといつかって愿うまま

0%

Restful API 简介

Restful是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用XML格式定义或JSON格式定义。Restful适用于移动互联网厂商作为业务使能接口的场景,实现第三方OTT调用移动网络资源的功能,动作类型为新增、变更、删除所调用资源。

Restful 特点

  1. 每一个URI代表1种资源;
  2. 客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源;
  3. 通过操作资源的表现形式来操作资源;
  4. 资源的表现形式是XML或者HTML;
  5. 客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息。

Restful 常用URL

操作 URL RequestMethod Restful URL Restful RequestMethod
查询 /user/query?name=tom GET /user?name=tom GET
详情 /user/getInfo?id=1 GET /user/1 GET
创建 /user/create?name=tom POST /user POST
修改 /user/update?id=1&name=jerry POST /user/1 PUT
删除 /user/delete?id=1 GET /user/1 DELETE

Spring相关常用注解

@RestController

标明Controller提供的是 Restful API

1
2
3
4
5
6
7
8
// UserController.java

@Slf4j
@Api(tags = "用户管理", description = "管理用户的基本信息")
@RestController
public class UserController {
// ...
}

@RequestMapping 及其变体

映射http请求的url到Java方法中

  • value: 指定request的地址
  • method: 指定请求的method类型, GET、POST、PUT、DELETE等
  • params: 指定request中包含的某些参数值,作为方法的输入,如:@RequestParam(value = "disable", required = false)
    • value: 为传入的参数
    • required: 设置为 false,若传入的 value 为空值,则报错;设置为 true,若传入的 value 为空值,则返回 null.
  • consumes: 指定处理请求的提交内容类型,例如 application/json, text/html;
  • produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
  • headers: 指定request中包含某些指定的header值,让该方法处理请求
1
2
3
4
5
6
7
8
9
10
11
// UserController.java

@RestController
public class UserController {

@ApiOperation(value = "查询所有用户信息", notes = "查询所有用户信息", httpMethod = "GET")
@RequestMapping(value = "/user",method = RequestMethod.GET)
public List<User> query() {
return getUsers();
}
}

随着Spring的发展,可以采用如下注解,简化@RequestMapping。

@GetMapping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// UserController.java

@RestController
public class UserController {

@GetMapping("/user/{id:\\d+}")
@JsonView(User.UserDetailView.class)
public User getInfo1(@PathVariable String id){
User user=new User();
user.setUsername("tom");

return user;
}
}

@PostMapping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// UserController.java

@RestController
public class UserController {

@PostMapping("/user")
public User create(@Valid @RequestBody User user, BindingResult errors){

if(errors.hasErrors()){
errors.getAllErrors().stream().forEach(error->System.err.println(error.getDefaultMessage()));
}
System.out.println(user.getId());
System.out.println(user.getUsername());
System.out.println(user.getPassword());
System.out.println(user.getBirthday());

user.setId(1);

return user;
}
}

@PutMapping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// UserController.java

@RestController
public class UserController {

@PutMapping("/user/{id:\\d+}")
public User update(@Valid @RequestBody User user, BindingResult errors){

if(errors.hasErrors()){
errors.getAllErrors().forEach(error->{
System.out.println(error.getDefaultMessage());
});
}
System.out.println(user.getId());
System.out.println(user.getUsername());
System.out.println(user.getPassword());
System.out.println(user.getBirthday());

user.setId(1);

return user;
}
}

@DeleteMapping

1
2
3
4
5
6
7
8
9
10
11
// UserController.java

@RestController
public class UserController {

@DeleteMapping("/user/{id:\\d+}")
public void delete(@PathVariable String id){
System.out.println(id);
}

}

@PatchMapping

Patch方式是对Put方式的一种补充。put方式是可以更新,但是更新的是整体。patch是对局部更新。

@RequestParam

映射请求参数到Java方法的参数中

@RequestParam(value=”参数名”,required=”true/false”,defaultValue=””)

  • value:参数名
  • required:是否包含该参数,默认为true,表示该请求路径中必须包含该参数,如果不包含就报错。
  • defaultValue:默认参数值,如果设置了该值,required=true将失效,自动为false,如果没有传该参数,就使用默认值
1
2
3
4
5
6
7
8
9
10
11
12
13
// UserController.java

@RestController
public class UserController {

@ApiOperation(value = "查询用户信息", notes = "根据用户名查询用户信息", httpMethod = "GET")
@GetMapping("/user")
public List<User> query(@RequestParam(name = "username",required = false,defaultValue = "tom") String nickname) {
System.out.println(nickname);
return getUsers();
}

}

@RequestBody

映射请求参数到Java的实体类中

该注解常用来处理Content-Type: 不是application/x-www-form-urlencoded编码的内容,例如application/json, application/xml等;

它是通过使用HandlerAdapter 配置的HttpMessageConverters来解析post data body,然后绑定到相应的bean上的。

因为配置有FormHttpMessageConverter,所以也可以用来处理application/x-www-form-urlencoded的内容,处理完的结果放在一个MultiValueMap<String, String>里,这种情况在某些特殊需求下使用,详情查看FormHttpMessageConverter api;

1
2
3
4
@RequestMapping(value = "/something", method = RequestMethod.PUT)
public void handle(@RequestBody String body, Writer writer) throws IOException {
writer.write(body);
}

@PathVariable ^7

@PageableDefault

指定分页参数默认值

  • page: 第几页,从0开始,默认为第0页
  • size: 每一页的大小,默认为20
  • sort: 排序相关的信息,以property,property(,ASC|DESC)的方式组织,例如sort=firstname&sort=lastname,desc表示在按firstname正序排列基础上按lastname倒序排列。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// UserController.java

@RestController
public class UserController {

@ApiOperation(value = "分页查询用户数据", notes = "分页查询用户数据", httpMethod = "GET")
@GetMapping("/user")
@JsonView(User.UserSimpleView.class)
public List<User> query(UserQueryCondition condition, @PageableDefault(page=1,size = 10,sort = "username,asc") Pageable pageable) {

// TEST:在控制台中打印不出来
System.out.println(pageable.getPageSize());
System.out.println(pageable.getPageNumber());
System.out.println(pageable.getSort());

return getUsers();
}

}

// User.java

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

public interface UserSimpleView{};

public interface UserDetailView extends UserSimpleView{};

private int id;

@MyConstraint(message = "这是一个测试")
@JsonView(UserSimpleView.class)
private String username;

@NotBlank(message = "密码不能为空")
@JsonView(UserDetailView.class)
private String password;

@Past(message = "生日必须是过去的时间")
private Date birthday;

}

// UserQueryCondition.java

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserQueryCondition {

private String username;

private int age;

private int ageTo;

private String xxx;
}

@Valid

Hibernate Validation注解

注解 说明
@NotNull 值不能为空
@Null 值必须为空
@Pattern(regex=) 字符串必须匹配正则表达式
@Size(min=,max=) 集合的元素数量必须在min和max之间
@CreditCardNumber(ignoreNoneDigitCharacters=) 字符串必须是信用卡号(美国标准)
@Email 字符串必须是Email地址
@Length(min=,max=) 检查字符串的长度
@NotBlank 字符串必须有字符
@NotEmpty 字符串不能为null,集合内须有元素
@Range(min=,max=) 值数字必须大于等于min,小于等于max
@SafeHtml 字符串是安全的html
@URL(protocol=,host=,port=,regexp=,flags=) 字符串是合法的url
@AssertFalse 值必须是False
@AssertTrue 值必须是True
@DecimalMax(value=,inclusive=) 值必须小于等于(inclusive=true)/小于(inclusive=false)value属性指定的值。可以注释在字符串类型的属性上。
@DecimalMin(value=,inclusive=) 值必须大于等于(inclusive=true)/大于(inclusive=false)value属性指定的值。可以注释在字符串类型的属性上。
@Digits(integer=,fraction=) 数字格式检查。integer指定整数部分额最大长度,fraction指定小数部分的最大长度
@Future 值必须是未来的日期
@Past 值必须是过去的日期
@Max(value=) 值必须小于等于value指定的值。不能注释在字符串类型的属性上。
@Min(value=) 值必须大于等于value指定的值。不能注释在字符串类型的属性上。

BindingResult

@Valid 和 BindingResult 是一一对应的,如果有多个@Valid,那么每个@Valid后面跟着的BindingResult就是这个@Valid的验证结果,顺序不能乱。

1
2
3
4
5
6
7
public Object doSomething(@Validated @RequestBody OneDto oneDto, 
BindingResult result) {
// 参数通不过校验也会进入方法执行,校验结果会通过result参数传递进来
if (result.hasErrors()){
// 没通过校验
}
}

@Validated和@Valid的区别

  • @Valid是使用Hibernate validation的时候使用
  • @Validated是只用Spring Validator校验机制使用

说明:java的JSR303声明了@Valid这类接口,而Hibernate-validator对其进行了实现。@Validation对@Valid进行了二次封装,在使用上并没有区别,但在分组、注解位置、嵌套验证等功能上有所不同,这里主要就这几种情况进行说明。

注解位置
  • @Validated:用在类型、方法和方法参数上。但不能用于成员属性(field),如果@Validated注解在成员属性上,则会报 不适用于field错误
  • @Valid:可以用在方法、构造函数、方法参数和成员属性(field)上
分组校验
  • @Validated:提供分组功能,可以在参数验证时,根据不同的分组采用不同的验证机制
  • @Valid:没有分组功能
组序列

默认情况下 不同级别的约束验证是无序的,但是在一些情况下,顺序验证却是很重要。

一个组可以定义为其他组的序列,使用它进行验证的时候必须符合该序列规定的顺序。在使用组序列验证的时候,如果序列前边的组验证失败,则后面的组将不再给予验证。

嵌套校验

一个待验证的pojo类,其中还包含了待验证的对象,需要在待验证对象上注解@Valid,才能验证待验证对象中的成员属性,这里不能使用@Validated。

HTTP状态码

GET

  • 安全且幂等
  • 获取表示
  • 变更时获取表示(缓存)
  • 200(OK) - 表示已在响应中发出
  • 204(无内容) - 资源有空表示
  • 301(Moved Permanently) - 资源的URI已被更新
  • 303(See Other) - 其他(如,负载均衡)
  • 304(not modified)- 资源未更改(缓存)
  • 400 (bad request)- 指代坏请求(如,参数错误)
  • 404 (not found)- 资源不存在
  • 406 (not acceptable)- 服务端不支持所需表示
  • 500 (internal server error)- 通用错误响应
  • 503 (Service Unavailable)- 服务端当前无法处理请求

POST

  • 不安全且不幂等
  • 使用服务端管理的(自动产生)的实例号创建资源
  • 创建子资源
  • 部分更新资源
  • 如果没有被修改,则不过更新资源(乐观锁)
  • 200(OK)- 如果现有资源已被更改
  • 201(created)- 如果新资源被创建
  • 202(accepted)- 已接受处理请求但尚未完成(异步处理)
  • 301(Moved Permanently)- 资源的URI被更新
  • 303(See Other)- 其他(如,负载均衡)
  • 400(bad request)- 指代坏请求
  • 404 (not found)- 资源不存在
  • 406 (not acceptable)- 服务端不支持所需表示
  • 409 (conflict)- 通用冲突
  • 412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)
  • 415 (unsupported media type)- 接受到的表示不受支持
  • 500 (internal server error)- 通用错误响应
  • 503 (Service Unavailable)- 服务当前无法处理请求

PUT

  • 不安全但幂等
  • 用客户端管理的实例号创建一个资源
  • 通过替换的方式更新资源
  • 如果未被修改,则更新资源(乐观锁)
  • 200 (OK)- 如果已存在资源被更改
  • 201 (created)- 如果新资源被创建
  • 301(Moved Permanently)- 资源的URI已更改
  • 303 (See Other)- 其他(如,负载均衡)
  • 400 (bad request)- 指代坏请求
  • 404 (not found)- 资源不存在
  • 406 (not acceptable)- 服务端不支持所需表示
  • 409 (conflict)- 通用冲突
  • 412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)
  • 415 (unsupported media type)- 接受到的表示不受支持
  • 500 (internal server error)- 通用错误响应
  • 503 (Service Unavailable)- 服务当前无法处理请求

DELETE

  • 不安全但幂等
  • 删除资源
  • 200 (OK)- 资源已被删除
  • 301 (Moved Permanently)- 资源的URI已更改
  • 303 (See Other)- 其他,如负载均衡
  • 400 (bad request)- 指代坏请求
  • 404 (not found)- 资源不存在
  • 409 (conflict)- 通用冲突
  • 500 (internal server error)- 通用错误响应
  • 503 (Service Unavailable)- 服务端当前无法处理请求

总结

  1. Restful是一种用URL描述资源的方式
  2. 使用HTTP方法描述行为,使用HTTP状态码来表示不同的结果
  3. 使用json交互数据
  4. Restful只是一种风格,并不是强制的标准

参考文献