目录
ResponseBodyAdvice 接口概述
ResponseBodyAdvice 快速使用
父pom文件
pom文件
ResponseDto
MyResponseBodyAdvice
DemoController
结果展示
ResponseBodyAdvice 接口概述
在实际项目中,我们经常需要在请求前后进行一些操作,比如:参数解密/返回结果加密、返回值封装,打印请求参数和返回结果的日志等。这些与业务无关的东西,我们不希望写在controller方法中,造成代码重复可读性变差。这里,我们经常使用@ControllerAdvice和RequestBodyAdvice、ResponseBodyAdvice来对请求前后进行处理(本质上就是AOP),来实现日志记录每一个请求的参数和返回结果。
1、ResponseBodyAdvice 接口允许在执行 @ResponseBody 或 ResponseEntity 控制器方法之后,但在使用 HttpMessageConverter 写入响应体之前自定义响应,进行功能增强。通常用于 加密,签名,统一数据格式等。
2、ResponseBodyAdvice 接口一共有两个方法:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.web.servlet.mvc.method.annotation;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
public interface ResponseBodyAdvice<T> {
boolean supports(MethodParameter var1, Class<? extends HttpMessageConverter<?>> var2);
@Nullable
T beforeBodyWrite(@Nullable T var1, MethodParameter var2, MediaType var3, Class<? extends HttpMessageConverter<?>> var4, ServerHttpRequest var5, ServerHttpResponse var6);
}
ResponseBodyAdvice 快速使用
1、使用方式:自定义类实现 ResponseBodyAdvice 接口,然后在类上标记 @ControllerAdvice 或@RestControllerAdvice 注解即可自动识别并进行功能增强。
2、下面以对返回数据封装统一格式为例进行演示(注意仅对返回值为 ResponseEntity 或者是有@ResponseBody 注解的控制器方法进行拦截,@RestController 相当于是类中的所有方法上都加了 @ResponseBody)。
3、注意如果控制层目标方法往外抛出了异常,则不再进入 ResponseBodyAdvice(需要使用@ExceptionHandler(value = Exception.class))
父pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<!-- <version>3.1.2</version>-->
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.chensir</groupId>
<artifactId>springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot</name>
<description>springboot</description>
<properties>
<java.version>8</java.version>
</properties>
<packaging>pom</packaging>
<modules>
<module>servlet</module>
<module>spring-interceptor</module>
<module>spring-aop</module>
<module>spring-united-reslut</module>
</modules>
<dependencies>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibaba-dingtalk-service-sdk</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.chensir</groupId>
<artifactId>springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>spring-united-reslut</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
ResponseDto<T>
// 泛型
@Data
public class ResponseDto<T> implements Serializable {
// 返回码(内部拟定)
private int code;
// 返回信息
private String message;
private T data;
}
MyResponseBodyAdvice
package com.chensir.advice;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.chensir.model.ResponseDto;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.servlet.http.HttpServletRequest;
// 拦截范围为 com.chensir.controller包下内容
@RestControllerAdvice(basePackages = {"com.chensir.controller"})
public class MyResponseBodyAdvice implements ResponseBodyAdvice {
// 是否开启拦截 true开启 false不开启
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
// 此处应该改为true,否则除了异常外 走到此处为false后就直接返回,也不再继续往下走了!
return true;
}
// 如果接口返回异常就在此处拦截 进行封装;value = Exception.class 对所有的异常均拦截!
@ExceptionHandler(value = Exception.class)
public Object defaultErrorHandler(HttpServletRequest req, Exception ex){
ResponseDto<Object> responseDto = new ResponseDto<>();
responseDto.setCode(501);
responseDto.setMessage(ex.getMessage());
return responseDto;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
// 有的接口再返回时会自己封装code,message,data。如果body是ResponseDto类型的实例,那么就直接返回body
if(body instanceof ResponseDto){
return body;
}
ResponseDto<Object> responseDto = new ResponseDto<>();
// 先用hutool定义为null
String message = StrUtil.EMPTY;
responseDto.setCode(0);
responseDto.setMessage(message);
responseDto.setData(body);
// 如果是string类型就用json封装一下;
if (aClass == StringHttpMessageConverter.class) {
return JSONUtil.toJsonStr(responseDto);
} else {
return responseDto;
}
}
}
DemoController
@RestController
@RequestMapping("/api")
public class DemoController {
@GetMapping("/demo1")
public String demo1() {
return "demo1";
}
@GetMapping("/demo2")
public List<Integer> demo2(){
ArrayList<Integer> arrayList = CollectionUtil.newArrayList(1, 2, 3, 4, 5, 6);
return arrayList;
}
@GetMapping("/demo3")
public Integer demo3(){
int a = 5/0;
return a;
}
}
结果展示