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

news2024/11/15 22:23:51

一、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 = WebResult.ok(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("/get3")
    @ApiOperation(value = "get3接口")
    public List get3() {
        log.info("get3接口");

        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("/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;
    }

}

在这里插入图片描述

– 求知若饥,虚心若愚。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PID各种算法的基本优缺点

PID时间系数对PID本身的影响 积分时间过小积分作用增强。 微分时间过大&#xff0c;微分控制作用过强&#xff0c;容易产生振荡。 在这里的时间系统&#xff0c;一般指的是采样的时间&#xff0c;也就是PID控制的周期。在无人机当中一般采用10ms控制一次。 一般来说采样周期越小…

PMP过了就是中级职称?

&#x1f33b;PMP项目管理专业人士认证在全球范围内受到广泛认可&#xff0c;许多人就误以为获得PMP证书就等同于获得中级职称。但是&#xff0c;事实真的如此吗❓ 1️⃣PMP不属于职称认证 ✅PMP证书&#xff1a; 是由美国项目管理协会(PMI)颁发的专业认证&#xff0c;旨在证明…

GeoServer发布地图服务(WMS、WFS)

文章目录 1. 概述2. 矢量数据源3. 栅格数据源 1. 概述 我们知道将GIS数据大致分成矢量数据和栅格数据&#xff08;地形和三维模型都是兼具矢量和栅格数据的特性&#xff09;。但是如果用来Web环境中&#xff0c;那么使用图片这个栅格形式的数据载体无疑是最为方便的&#xff0…

入库和出库的成本对不上如果如何解决

入库是前期手工录入的车价是对的&#xff0c;出库是根据销售出库单生成的 入库成本和出库成本不一致的解决方法 解决方法&#xff1a; 整车管理——正车库存——库存核算——整车出库 成本核算

FC SAN光纤交换机维护介绍

一、什么是FC SAN &#xff1f; ​存储区域网络&#xff08;Storage Area Network&#xff0c;SAN&#xff09;采用网状通道&#xff08;Fibre Channel &#xff0c;简称FC&#xff0c;区别与Fiber Channel光纤通道&#xff09;技术&#xff0c;通过FC交换机连接存储阵列和服务…

QT上位机开发(会员充值软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 所有的控件当中&#xff0c;除了label、edit、radio、combobox和button之外&#xff0c;另外一个用的比较多的控件就是grid&#xff0c;也可称之为…

李沐-《动手学深度学习-02-目标检测

一 、目标检测算法 1. R-CNN a . 算法步骤 使用启发式搜索算法来选择锚框&#xff08;选出多个锚框大小可能不一&#xff0c;需要使用Rol pooling&#xff09;使用预训练好的模型&#xff08;去掉分类层&#xff09;对每个锚框进行特征抽取&#xff08;如VGG,AlexNet…)训练…

CAD安装教程

CAD安装教程 目录 一&#xff0e; 下载CAD二&#xff0e; 安装CAD 一&#xff0e; 下载CAD 如果需要CAD安装包请私信。 二&#xff0e; 安装CAD 解压压缩包AutoCAD2022中文版&#xff0c;以管理员身份运行AutoCAD_2022_Simplified_Chinese_Win_64bit_dlm.sfx。 选择解压路径。…

DockerUI本地如何部署并结合内网穿透实现远程访问管理界面

文章目录 前言1. 安装部署DockerUI2. 安装cpolar内网穿透3. 配置DockerUI公网访问地址4. 公网远程访问DockerUI5. 固定DockerUI公网地址 前言 DockerUI是一个docker容器镜像的可视化图形化管理工具。DockerUI可以用来轻松构建、管理和维护docker环境。它是完全开源且免费的。基…

NACHI机器人虚拟示教器报I2101异常

前言 机器人示教器报&#xff1a;I2101 异常停止按钮或外部停止信号被输入 无法再示教模式下进行程序的运行&#xff01; 解决方法 结果 最后测试可以正常的运行程序

大模型实战营Day2 轻松玩转书生·浦语大模型趣味Demo

大模型&#xff1a;参数数量巨大&#xff0c;拥有庞大计算能力和参数规模的模型 InternLM &#xff1a;是一个开源的轻量级训练框架&#xff0c;旨在支持模型预训练&#xff0c;而无需广泛的依赖关系。通过单一代码库&#xff0c;它支持在具有数千个 GPU 的大规模集群上进行预训…

Spark回归分析与特征工程

回归分析是统计学和机器学习中的一个重要分支&#xff0c;用于建立因变量与自变量之间的关系模型。在大数据领域&#xff0c;Apache Spark为回归分析提供了强大的工具和库&#xff0c;以处理大规模数据集。本文将深入探讨如何使用Spark进行回归分析以及如何进行特征工程&#x…

论文阅读记录SuMa SuMa++

首先是关于SuMa的阅读&#xff0c;SuMa是一个完整的激光SLAM框架&#xff0c;核心在于“基于面元(surfel)”的过程&#xff0c;利用3d点云转换出来的深度图和法向量图来作为输入进行SLAM的过程&#xff0c;此外还改进了后端回环检测的过程&#xff0c;利用提出的面元的概念和使…

软件测试|Windows系统安装Cypress教程

前言 每当提起web自动化测试&#xff0c;大家首先想到的就是selenium&#xff0c;最近这两年时间&#xff0c;出现了playwright和cypress这两款新的工具&#xff0c;不过现在应用范围还是不如selenium&#xff0c;之前我们介绍了playwright的使用&#xff0c;现在开始&#xf…

【c++】list的特性及使用

目录 一、list的介绍 二、list的深度剖析与模拟实现 1、list图解 2、list增删查改模拟实现 三、list与vector的对比 一、list的介绍 STL中的list指的是带头双向循环链表。list是可以在常数范围内任意位置进行插入和删除的序列式容器&#xff0c;并且可以前后双向迭代。lis…

【AI视野·今日CV 计算机视觉论文速览 第283期】Thu, 4 Jan 2024

AI视野今日CS.CV 计算机视觉论文速览 Thu, 4 Jan 2024 Totally 85 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computer Vision Papers LEAP-VO: Long-term Effective Any Point Tracking for Visual Odometry Authors Weirong Chen, Le Chen, Rui Wang, Marc P…

大数据可视化Web框架——飞致云Dataease在Windows端的安装指南(多图说明版)V2.2最新版

DataEase开源代码在Windows系统开发环境搭建与调试指南_怎么部署dataease 2.0-CSDN博客https://blog.csdn.net/tllhc01/article/details/135220598?spm1001.2014.3001.5502参考这一篇&#xff0c;基于dataease2.2源码进行构建 需要先下载三个文件&#xff0c;且版本一一对应均…

性能分析与调优: Linux 监测工具的数据来源

目录 一、实验 1.环境 2. proc目录 3. sys目录 4.netlink 5.tracepoint 6.kprobes 7. uprobes 二、问题 1.systemd如何查看启动时间 2.CentOS与Ubuntu如何安装bpftrace 3.snap有哪些常用的命令 4.snap如何安装store 5.如何列出使用bpftracede的OpenJDK USDT探针 …

机器学习笔记:时间序列异常检测

1 异常类型 1.1 异常值outlier 给定输入时间序列&#xff0c;异常值是时间戳值其中观测值与该时间序列的期望值不同。 1.2 波动点&#xff08;Change Point&#xff09; 给定输入时间序列&#xff0c;波动点是指在某个时间t&#xff0c;其状态在这个时间序列上表现出与t前后…

基于JSP+Servlet+Mysql的学生信息管理系统

基于JSPServletMysql的学生信息管理系统 一、系统介绍二、功能展示1.目录2.数据库3.登陆4.注册5.主页 四、其它1.其他系统实现五.获取源码 一、系统介绍 项目名称&#xff1a;基于JSPServletMysql的学生信息管理系统 项目架构&#xff1a;B/S架构 开发语言&#xff1a;Java语…