SpringBoot全局Controller返回值格式统一处理

一、Controller返回值格式统一

1、WebResult类

在 Controller对外提供服务的时候,我们都需要统一返回值格式。一般定义一个 WebResult类。

统一返回值(WebResult类)格式如下:

{
  "success": true,
  "code": 200000,
  "message": null,
  "data": {
    "pageList": [
      "张三",
      "ccc"
    ],
    "paginator": {
      "currentPage": 1,
      "pageSize": 2,
      "total": 3,
      "pages": 4
    }
  }
}

WebResult类信息:

@Data
@ApiModel(value = "Web结果集")
public class WebResult<T> implements Serializable {
    private static final long serialVersionUID = -4350499690382193884L;

    /**
     * 是否成功, 默认false
     */
    @ApiModelProperty(value = "是否成功, 默认false")
    private Boolean success = false;

    /**
     * 返回状态码
     */
    @ApiModelProperty(value = "返回状态码")
    private Integer code;

    /**
     * 返回信息
     */
    @ApiModelProperty(value = "返回信息")
    private String message;

    /**
     * 返回数据
     */
    @ApiModelProperty(value = "返回数据")
    private T data;

    public static <T> WebResult<T> ok() {
        WebResult<T> webResult = new WebResult<>();
        webResult.setSuccess(true);
        webResult.setCode(WebHttpCode.SERVICE_SUCCESS);
        webResult.setMessage("操作成功");
        return webResult;
    }

    public static <T> WebResult<T> ok(T data) {
        WebResult<T> webResult = new WebResult<>();
        webResult.setSuccess(true);
        webResult.setCode(WebHttpCode.SERVICE_SUCCESS);
        webResult.setMessage("操作成功");
        webResult.setData(data);
        return webResult;
    }

    public static <T> WebResult<T> error() {
        WebResult<T> webResult = new WebResult<>();
        webResult.setSuccess(false);
        webResult.setCode(WebHttpCode.SERVICE_ERROR);
        webResult.setMessage("操作失败");
        return webResult;
    }

    public WebResult message(String message) {
        this.setMessage(message);
        return this;
    }

    public WebResult data(T data) {
        this.setData(data);
        return this;
    }

}

如果我们不做全局 Controller统一处理返回时,就出需要业务在每个 Controller类中返回 WebResult类。同时在全局异常中也返回 WebResult类。

有没有一种跟优雅的方式处理呢?当然有

  • 业务不需要对所有 Controller都使用一个返回值(WebResult类),Controller可以需要返回原始值或者自定义的基类,然后处理器统一对返回值(WebResult类)进行封装并输出。
  • 同时也可以添加自定义注解,此注解用于忽略返回值封装,按照 Controller原始值返回。

下面我们使用 HandlerMethodReturnValueHandler接口来实现。

2、相关类说明

2.1 HandlerMethodReturnValueHandler接口

使用不同策略处理从调用处理程序方法的返回值,策略处理顶层接口,自定义返回值格式需要实现此接口。

org.springframework.web.method.support.HandlerMethodReturnValueHandler

  • supportsReturnType:设置支持返回值类型
  • handleReturnValue:处理返回值基础参数

在这里插入图片描述

2.2 RequestMappingHandlerAdapter类

请求映射处理适配,包含了参数、返回值处理器等信息

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

  • HandlerMethodReturnValueHandlerComposite内部维护了HandlerMethodReturnValueHandler列表

在这里插入图片描述

3.3 RequestResponseBodyMethodProcessor类

属于HandlerMethodReturnValueHandler子类。

org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor

  • 主要功能是对请求和响应体的做处理的方法,所有属于RequestResponseBodyMethodProcessor的子类都需要替换为自定义返回值处理

在这里插入图片描述

全局Controller返回值统一处理实现原理就是:在bean初始化的时候,获取到所有处理器数组,然后将所有是 RequestResponseBodyMethodProcessor处理器子类对返回值处理的过程替换为自定义返回值处理器处理。
这样当调用对应返回值处理器时,将会使用到自定义的返回值处理器,也就是所有返回值都会按照规定的进行处理。
同时,我们也可以自定义注解(作用于Controller类或者方法级别忽略返回值封装),然后在自定义返回值处理器中忽略返回值封装。

二、全局Controller返回值统一处理实战

1、自定义注解

自定义注解,作用于Controller类或者方法级别忽略返回值封装。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IgnoreResponseBodyWrap {
}

2、创建自定义返回值处理器类

创建自定义处理器类实现 HandlerMethodReturnValueHandler接口,主要用于实现自定义返回值内容,不需要注入容器。

下面代码中的 BaseXXXResult是业务的基类。你可以自定义或者使用你们约定的基类。

/**
 * Controller 返回值格式统一处理
 */
public class ResponseBodyWrapHandler implements HandlerMethodReturnValueHandler {

    private final HandlerMethodReturnValueHandler delegate;

    public ResponseBodyWrapHandler(HandlerMethodReturnValueHandler delegate) {
        this.delegate = delegate;
    }

    /**
     * 设置支持返回值类型
     *
     * @param returnType
     * @return
     */
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return delegate.supportsReturnType(returnType);
    }

    /**
     * 处理返回值基础参数
     *
     * @param returnValue  方法的返回值对象
     * @param returnType   封装方法参数说明的辅助类(方法的返回值类型)
     * @param mavContainer 用于设置模型和视图的容器
     * @param webRequest   当前的请求对象
     * @throws Exception
     */

    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        // 如果类或者方法含有不包装注解则忽略包装
        IgnoreResponseBodyWrap ignoreResponseBodyWrap = returnType.getDeclaringClass().getAnnotation(IgnoreResponseBodyWrap.class);
        if (ignoreResponseBodyWrap != null) {
            delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
            return;
        }
        ignoreResponseBodyWrap = returnType.getMethodAnnotation(IgnoreResponseBodyWrap.class);
        if (ignoreResponseBodyWrap != null) {
            delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
            return;
        }

        // 返回统一格式
        Object obj = null;
        if (returnValue instanceof WebResult) {
            obj = returnValue;
        } else if (returnValue instanceof BasePageResult) {
            BasePageResult basePageResult = (BasePageResult) returnValue;
            ErrorCode errorCode = basePageResult.getErrorCode();
            Map<String, Object> pageData = new HashMap<>();
            pageData.put(CommonConstants.PAGE_LIST, basePageResult.getPageList());
            pageData.put(CommonConstants.PAGINATOR, basePageResult.getPaginator());
            obj = WebResult.ok().data(pageData).message(errorCode == null ? null : errorCode.getMessage());
        } else if (returnValue instanceof BaseOperateResult) {
            BaseOperateResult baseOperateResult = (BaseOperateResult) returnValue;
            ErrorCode errorCode = baseOperateResult.getErrorCode();
            boolean success = baseOperateResult.isSuccess();
            if (success) {
                obj = WebResult.ok().data(baseOperateResult.getId()).message(errorCode == null ? null : errorCode.getMessage());
            } else {
                obj = WebResult.error().message(errorCode == null ? null : errorCode.getMessage());
            }
        } else if (returnValue instanceof BaseDataResult) {
            BaseDataResult baseDataResult = (BaseDataResult) returnValue;
            ErrorCode errorCode = baseDataResult.getErrorCode();
            boolean success = baseDataResult.isSuccess();
            if (success) {
                obj = WebResult.ok().data(baseDataResult.getData()).message(errorCode == null ? null : errorCode.getMessage());
            } else {
                obj = WebResult.error().message(errorCode == null ? null : errorCode.getMessage());
            }
        } else if (returnValue instanceof BaseResult) {
            BaseResult baseResult = (BaseResult) returnValue;
            ErrorCode errorCode = baseResult.getErrorCode();
            boolean success = baseResult.isSuccess();
            if (success) {
                obj = WebResult.ok().message(errorCode == null ? null : errorCode.getMessage());
            } else {
                obj = WebResult.error().message(errorCode == null ? null : errorCode.getMessage());
            }
        } else {
            obj = returnValue;
        }

        delegate.handleReturnValue(obj, returnType, mavContainer, webRequest);
    }
}

3、注册自定义返回值处理器类

将所有 RequestResponseBodyMethodProcessor返回值处理器替换为自定义的返回值处理器。

@Component
public class ResponseBodyWrapFactoryBean implements InitializingBean {
    private final RequestMappingHandlerAdapter adapter;

    @Autowired
    public ResponseBodyWrapFactoryBean(RequestMappingHandlerAdapter adapter) {
        this.adapter = adapter;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers();
        if (returnValueHandlers.size() > 0) {
            // 将内置的返回值处理器进行替换
            List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(returnValueHandlers);
            decorateHandlers(handlers);
            adapter.setReturnValueHandlers(handlers);
        }
    }

    /**
     * 将所有RequestResponseBodyMethodProcessor返回值处理器替换为自定义的返回值处理器
     *
     * @author tianxincode@163.com
     * @since 2020/10/12
     */
    private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) {
        for (HandlerMethodReturnValueHandler handler : handlers) {
            if (handler instanceof RequestResponseBodyMethodProcessor) {
                // 替换为自定义返回值处理器
                ResponseBodyWrapHandler decorator = new ResponseBodyWrapHandler(handler);
                int index = handlers.indexOf(handler);
                handlers.set(index, decorator);
                break;
            }
        }
    }

}

4、全局异常处理类

一般项目都会有一个全局异常处理类

@Slf4j
@ResponseBody
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    private WebResult handlerException(Exception e) {
        log.error("Exception 异常 -> error:", e);
        WebResult webResult = new WebResult();
        webResult.setSuccess(false);
        webResult.setCode(WebHttpCode.SYSTEM_ERROR);
        webResult.setMessage("系统异常,请联系管理员");
        return webResult;
    }

    @ExceptionHandler(value = RuntimeException.class)
    private WebResult handlerRuntimeException(RuntimeException e) {
        log.error("RuntimeException 异常 -> error:", e);
        WebResult webResult = new WebResult();
        webResult.setSuccess(false);
        webResult.setCode(WebHttpCode.SYSTEM_ERROR);
        webResult.setMessage("系统异常");
        return webResult;
    }


    @ExceptionHandler(value = ServiceException.class)
    private WebResult handlerServiceException(ServiceException e) {
        log.error("ServiceException 异常 -> error:", e);
        WebResult webResult = new WebResult();
        webResult.setSuccess(false);
        webResult.setCode(WebHttpCode.SERVICE_ERROR);
        webResult.setMessage(e.getMessage());
        return webResult;
    }

}

5、Controller示例

@RestController
@RequestMapping("/project2")
@Slf4j
@Api(value = "Project2Controller", tags = {"Project2相关操作接口"})
public class Project2Controller {


    @GetMapping("/successPage")
    @ApiOperation(value = "successPage接口")
    public BaseResult successPage() {
        log.info("successPage接口");

        List list = new ArrayList();
        list.add("张三");
        list.add("ccc");

        BasePageResult basePageResult = new BasePageResult<>();
        basePageResult.setSuccess(Boolean.TRUE);

        basePageResult.setPageList(list);
        basePageResult.setPaginator(new Paginator(1, 2, 3, 4));
        return basePageResult;
    }

    @GetMapping("/get")
    @ApiOperation(value = "get接口")
    @IgnoreResponseBodyWrap
    public List get() {
        log.info("get接口");

        ProjectDO projectDO = new ProjectDO();
        projectDO.setId(10L);
        projectDO.setName("项目10");
        projectDO.setDepartmentId(0L);
        projectDO.setDescr("");
        projectDO.setDelFlag(false);
        projectDO.setCreateTime(new Date());
        projectDO.setUpdateTime(new Date());


        List list = new ArrayList();
        list.add("张三");
        list.add("ccc");
        return list;
    }

    @GetMapping("/get2")
    @ApiOperation(value = "get2接口")
    public BaseDataResult get2() {
        log.info("get2接口");

        ProjectDO projectDO = new ProjectDO();
        projectDO.setId(10L);
        projectDO.setName("项目10");
        projectDO.setDepartmentId(0L);
        projectDO.setDescr("");
        projectDO.setDelFlag(false);
        projectDO.setCreateTime(new Date());
        projectDO.setUpdateTime(new Date());

        BaseDataResult baseDataResult = new BaseDataResult();
        baseDataResult.setSuccess(Boolean.TRUE);
        baseDataResult.setData(projectDO);

        return baseDataResult;
    }

    /**
     * 文件下载接口
     */
    @GetMapping("/download")
    @ApiOperation(value = "文件下载接口")
    public void download(HttpServletResponse response) throws SerialException {

        try {
            // 获取要下载的文件
            File file = new File("D:\\TempFiles\\xxxxxx.xlsx");

            String fileName = file.getName();
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("UTF-8"), "iso-8859-1"));

            IOUtils.copy(new FileInputStream(file), response.getOutputStream());
        } catch (Exception e) {
            log.info("文件下载异常 -> e=", e);
            throw new ServiceException("文件下载异常");
        }

    }



    @GetMapping("/systemError")
    @ApiOperation(value = "systemError接口")
    public BaseResult systemError() {
        log.info("systemError接口");

        int i = 2 / 0;

        BaseResult baseResult = new BaseResult();
        baseResult.setSuccess(Boolean.TRUE);
        return baseResult;
    }

}

在这里插入图片描述

– 求知若饥,虚心若愚。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/291826.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Mysql事务transaction简介

文章目录 什么是事务针对Mysql隔离级别读未提交读提交可重复读串行化 mysql中的数据结构索引数据结构mysql中的锁种类**共享锁和独占锁**表锁、行锁(记录锁、间隙锁、临键锁) spring中的事务事务特性 什么是事务 事务是一个不可分割的数据库操作序列&#xff0c;也是数据库并发…

element-ui Tree 树形控件 过滤保留子级并获取过滤后的数据 多选改单选

本示例基于vue2 element-ui element-ui 的官网demo是只保留到过滤值一级的&#xff0c;并不会保留其子级 目标 1、Tree 树形控件 保留过滤值的子级 2、在第一次过滤数据的基础上进行第二次过滤 3、Tree 树形控件 多选改为单选&#xff0c;且只有最末端子级可以选择 不足…

【Spring】AOP的AspectJ开发

AOP基础不了解可以阅读&#xff1a;【Spring】AOP原来如此-CSDN博客 AspectJ是一个居于JAVA开发的AOP框架 基于XML的声明式AspectJ 基于XML的声明式AspectJ是通过XML文件来定义切面&#xff0c;切入点及通知&#xff0c;所有的切面、切入点和通知必须定义在内&#xff0c; 元…

Android Jetpack学习系列——Navigation

写在前面 Google在2018年就推出了Jetpack组件库&#xff0c;但是直到今天我才给重视起来&#xff0c;这真的不得不说是一件让人遗憾的事。过去几年的空闲时间里&#xff0c;我一直在尝试做一套自己的组件库&#xff0c;帮助自己快速开发&#xff0c;虽然也听说过Jetpack&#…

第一课:Transformer

第一课&#xff1a;Transformer 文章目录 第一课&#xff1a;Transformer1、学习总结&#xff1a;什么是语言模型&#xff1f;大语言模型&#xff08;LLM&#xff09;技术演变史注意力机制Transformer结构课程ppt及代码地址 2、学习心得&#xff1a;3、经验分享&#xff1a;4、…

【DevOps-02】Code编码阶段工具

一、简要说明 在code阶段,我们需要将不同版本的代码存储到一个仓库中,常见的版本控制工具就是SVN或者Git,这里我们采用Git作为版本控制工具,GitLab作为远程仓库。 Git安装安装GitLab配置GitLab登录账户二、Git安装 Git官网 Githttps://git-scm.com/

移动通信原理与关键技术学习(2)

1.多径信道滤波器表示&#xff0c;多径信道可以认为是线性时变滤波器&#xff0c;接收信号为发送信号与信道冲激响应的卷积。 2.调制就是对信号源的信息进行处理加到载波上&#xff0c;使其变为适合于信道传输的形式的过程&#xff0c;就是使载波随信号而改变的技术。 3.进行调…

VUE 若依框架,当页面设置了keepAlive=true,v-if和v-hasPermi作用在统一个按钮上时v-hasPermi失效,出现按钮显示异常问题

当前列表页设置了缓存keepAlivetrue&#xff0c;同时&#xff0c;在同一个按钮上使用v-if判断数据状态、用v-hasPermi判断按钮权限 当v-if的数据状态改变&#xff0c;由 1 变成 2 的时候&#xff0c;后面的v-hasPermi判断失效 原因&#xff1a; 是因为一开始页面初始化时&#…

HTML5+CSS3⑥——CSS三大特性、表格、列表

CSS特性 继承性 层叠性 优先级 叠加计算规则 表格 表格结构标签 合并单元格 列表 无序列表 有序列表 定义列表

显著提升VMware虚拟机运行速度的技巧

最主要是要把CPU核心减少到2&#xff0c;以前设置为4非常卡。因为我的电脑一个就4个CPU。

听GPT 讲Rust源代码--compiler(11)

File: rust/compiler/rustc_mir_transform/src/simplify.rs 在Rust源代码中&#xff0c;rust/compiler/rustc_mir_transform/src/simplify.rs文件是Rust编译器中一系列进行MIR&#xff08;中间表示&#xff09;简化的转换的实现。MIR是Rust编译器中用于进行优化和代码生成的中间…

Python遍历读取 A 文件夹中的 A1、A2、A3、A4、A5 中的各子文件夹中的图片,并对每张图片处理后保存到指定路径

目录 一、具体步骤二、文件夹目录结构样例三、代码四、实例遍历处理后结果五、总结 一、具体步骤 首先&#xff0c;指定 A 文件夹的路径和重命名后的文件夹路径。 然后&#xff0c;遍历 A 文件夹中的各子文件夹。 在每个子文件夹中&#xff0c;遍历所有文件。 读取每个文件&am…

电路分析竟然这么简单?还可以用软件仿真~

同学们大家好&#xff0c;今天我们继续学习杨欣的《电子设计从零开始》&#xff0c;这本书从基本原理出发&#xff0c;知识点遍及无线电通讯、仪器设计、三极管电路、集成电路、传感器、数字电路基础、单片机及应用实例&#xff0c;可以说是全面系统地介绍了电子设计所需的知识…

【MongoDB】关于MongoDB更新文档update的操作,十分详细,建议收藏!!!

&#x1f601; 作者简介&#xff1a;一名大四的学生&#xff0c;致力学习前端开发技术 ⭐️个人主页&#xff1a;夜宵饽饽的主页 ❔ 系列专栏&#xff1a;MongoDB数据库学习 &#x1f450;学习格言&#xff1a;成功不是终点&#xff0c;失败也并非末日&#xff0c;最重要的是继…

UDP单播

CMakeLists.txt文件中添加如下行&#xff1a; link_libraries(ws2_32) 1.发送端 #include <iostream> #include <winsock2.h> #include <cstdio>#pragma comment(lib, "Ws2_32.lib") // Link with ws2_32.libint main() {1.Initialize winsock…

googlecode.log4jdbc慢sql日志,格式化sql

前言 无论使用原生JDBC、mybatis还是hibernate&#xff0c;使用log4j等日志框架可以看到生成的SQL&#xff0c;但是占位符和参数总是分开打印的&#xff0c;不便于分析&#xff0c;显示如下的效果: googlecode Log4jdbc 是一个开源 SQL 日志组件&#xff0c;它使用代理模式实…

AI的突破与融合:2024年中国智能技术的新纪元_光点科技

随着人工智能领域的不断突破&#xff0c;2024年注定将成为中国智能技术发展的一个新纪元。当下&#xff0c;AI技术不仅在理论研究上取得了重大进展&#xff0c;其在商业应用、社会服务等领域的融合也日益深入。本文将结合近期网络上的AI热点&#xff0c;展望中国在AI技术方面的…

Springboot整合RocketMQ 基本消息处理

目录 1. 同步消息 2. 异步消息 3. 单向消息 4. 延迟消息 5. 批量消息 6. 顺序消息 7. Tag过滤 导入依赖 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId></dependency> …

CNN——AlexNet

1.AlexNet概述 论文原文&#xff1a;ImageNet Classification with Deep Convolutional Neural Networks 在LeNet提出后&#xff0c;卷积神经网络在计算机视觉和机器学习领域中很有名气。但卷积神经网络并没有主导这些领域。这是因为虽然LeNet在小数据集上取得了很好的效果&am…

保姆级教程:从0到1搭建web自动化测试环境

之前都是在linux上安装&#xff0c;第一次在windows上配置环境&#xff0c;加上距离上次配置环境有点久了&#xff0c;竟也花了点时间。特此记录下保姆级教程&#xff0c;给初学者一个有效的参考&#xff01; 一. 环境搭建 工具清单 工具工具名版本Java开发工具包JDK1.8浏览…