目录

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;
    }
}

结果展示

 

 

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐