上篇我们学习了《32-Spring MVC的控制器建言@ControllerAdvice》,本篇我们学习REST控制器建言@RestControllerAdvice。
@RestControllerAdvice是组合注解,它组合了@ControllerAdvice和@ResponseBody,它的功能和@ControllerAdvice一致,主要使用于对RESTful的请求体和返回体进行定制处理。
2.4.1 先处理请求体与后处理返回体
对请求体定制处理实现RequestBodyAdvice接口,它会在请求体进入控制器方法之前对请求体进行先处理;对返回体进行定制处理ResponseBodyAdvice,它会在控制器方法返回值确定之后对返回值进行后处理。他们和@RestControllerAdvice一起使用。
我们定义个注解,这个注解作为使用我们的定制功能的标记。
@Target({ElementType.PARAMETER, ElementType.METHOD}) //支持注解在方法参数和方法上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ProcessTag {
}
定制请求体的建言:
@RestControllerAdvice
public class CustomRequestBodyAdvice implements RequestBodyAdvice {
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return methodParameter.getParameterAnnotation(ProcessTag.class) != null; //1
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
return inputMessage; //2
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
if (body instanceof Person) {
Person person = (Person) body;
String upperCaseName = person.getName().toUpperCase();
return new Person(person.getId(), upperCaseName, person.getAge());
}
return body; //3
}
@Override
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
if(Person.class.isAssignableFrom((Class<?>) targetType)){
return new Person(new Random().nextLong(),"Nobody",-1);
}
return body; //4
}
}
- 该请求体建言起效的条件,本例是请求体参数是否标记了@ProcessTag注解;
- 在请求体读取前,未做任何处理;
- 在请求体读取后,如果请求体类型是Person,将name转为大写;若不是Person保持不变;
- 在处理请求体为空时,如果请求体类型是Person,建一个对象,否则保持不变。
定制的返回体处理建言:
@RestControllerAdvice
public class CustomResponseBodyAdvice implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return returnType.hasMethodAnnotation(ProcessTag.class); //1
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof Person){
Map<String, Object> map = new HashMap<>();
map.put("person", body);
map.put("extra-response-body", "demo-body");
return map;
}
return body; //2
}
}
- 该返回体建言起效的条件,本例是控制器方法上标记了@ProcessTag注解;
- 在写返回体前,我们将额外的信息和body封装到map里返回;若不是Person类型则保持不变。
我们在控制里验证这两个建言:
@GetMapping("/modifyBodies")
@ProcessTag //标记定制返回体
public Person modifyRequestBody(@ProcessTag @RequestBody Person person){ //标记定制请求体
return person;
}
我们请求体不为空时请求:
我们请求体为空时请求: