接口返回响应,统一封装(ResponseBodyAdvice + Result)(SpringBoot)

这篇具有很好参考价值的文章主要介绍了接口返回响应,统一封装(ResponseBodyAdvice + Result)(SpringBoot)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

需求

接口的返回响应,封装成统一的数据格式,再返回给前端。

依赖

对于SpringBoot项目,接口层基于 SpringWeb,也就是 SpringMVC

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

说明

为了使接口的返回结果数据更加规范化,便于接口测试和前端处理,需要以统一的格式来返回数据;

为了不在每一个接口里面,都写一段返回数据封装的代码,将数据封装的逻辑提取出来,使用面相切面原理(AOP),统一对数据进行封装。

如上,涉及到两个问题:

  1. 定义:响应实体的数据结构;
  2. 响应数据统一封装;

下面,我们分别来介绍这两个问题如何处理。

响应实体的数据结构

数据结构

返回响应,统一封装实体,数据结构如下:
接口返回响应,统一封装(ResponseBodyAdvice + Result)(SpringBoot),Spring Boot,spring boot,后端,java

代码

package com.example.core.model;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;

/**
 * 返回响应,统一封装实体
 *
 * @param <T> 数据实体泛型
 */
@Getter
@ToString
@EqualsAndHashCode
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Schema(name = "返回响应", description = "返回响应,统一封装实体")
public class Result<T> {

    @Schema(description = "用户提示", example = "操作成功!")
    private String userMessage;

    /**
     * 错误码<br>
     * 调用成功时,为 null。<br>
     * 示例:A0211
     */
    @Schema(description = "错误码")
    private String errorCode;

    /**
     * 错误信息<br>
     * 调用成功时,为 null。<br>
     * 示例:"用户输入密码错误次数超限"
     */
    @Schema(description = "错误信息")
    private String errorMessage;

    /**
     * 数据实体(泛型)<br>
     * 当接口没有返回数据时,为 null。
     */
    @Schema(description = "数据实体(泛型)")
    private T data;


    public static <T> Result<T> success(T data) {
        return new Result<>("操作成功!", null, null, data);
    }


    public static <T> Result<T> fail(String userMessage, String errorCode, String errorMessage) {
        return new Result<>(userMessage, errorCode, errorMessage, null);
    }

}

特别说明:不需要表示成功或失败的字段

在本处的数据结构中,没有一个专门用来表示接口请求成功或失败的字段(比如:success 或 code)。

推荐的做法是:使用 HTTP状态码表示请求是否成功;最简单的模型是,当状态码为200时,表示成功;当状态码为 3xx,4xx,5xx 时,代表请求失败。

HTTP的状态码,已经清晰的描述了请求的响应状态(成功/失败)。

复杂响应模型中, HTTP状态码

复杂模型中, HTTP状态码还包含请求成功的类型和失败的原因。

复杂模型中,请求成功的状态码及含义:

HTTP状态码 含义
200 OK 请求成功
201 Created 新增成功
202 Accepted 成功,异步任务已经接收

请求失败的状态码及含义

HTTP状态码 含义
400 Bad Request 失败,客户端请求错误(比如,参数传递错误)
401 Unauthorized 失败,未登录
403 Forbidden 失败,未授权
405 Method Not Allowed 失败,Http请求方法不支持
500 Internal Server Error 失败,内部服务器错误

405 Method Not Allowed:当一个请求,能找到对应的【接口路径】,但是没有找到对应的【请求方法】时,会报异常,返回响应码为 405 。
500 Internal Server Error:服务器内部出现了错误,返回响应码为 500。

响应统一封装

响应统一封装:基于 ResponseBodyAdvice

基于面相切面编程(AOP)原理,每个接口方法调用成功后,在返回给客户端前,会进行指定的处理,这里是响应数据统一封装成指定的格式;其实也可以做其他的事情,比如 加密。

代码

package com.example.core.advice;


import com.example.core.model.Result;
import com.example.core.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
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.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * 响应统一封装
 * <p>
 * 将响应数据,封装成统一的数据格式。
 * <p>
 * 通过本处理器,将接口方法返回的数据,统一封装到 Result 的 data 字段中,如果接口方法返回为 void,则 data 字段的值为 null。
 */
@Slf4j
@RestControllerAdvice(basePackages = "com.example.web")
public class GlobalResponseHandler implements ResponseBodyAdvice<Object> {

    /**
     * 此组件是否支持给定的控制器方法返回类型和选定的 {@code HttpMessageConverter} 类型。
     *
     * @return 如果应该调用 {@link #beforeBodyWrite} ,则为 {@code true};否则为false。
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 返回类型不为Result,才需要封装
        return returnType.getParameterType() != Result.class;
    }


    /**
     * 统一封装返回响应数据
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
                                  ServerHttpResponse response) {

        // 数据封装为Result:将接口方法返回的数据,封装到 Result.data 字段中。
        Result<Object> result = Result.success(body);

        // 返回类型不是 String:直接返回
        if (returnType.getParameterType() != String.class) {
            return result;
        }

        // 返回类型是 String:不能直接返回,需要进行额外处理
        // 1. 将 Content-Type 设为 application/json ;返回类型是String时,默认 Content-Type = text/plain
        HttpHeaders headers = response.getHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        // 2. 将 Result 转为 Json字符串 再返回
        // (否则会报错 java.lang.ClassCastException: com.example.core.model.Result cannot be cast to java.lang.String)
        return JsonUtil.toJson(result);
    }

}

补充说明

需要注意两点:

  1. 返回类型不为 Result,才需要封装;
  2. 返回类型是 String,需要进行额外处理,不能直接返回,否则会报错。

如果返回类型是 Result 也封装,就会使得接口返回中多一层 Result 嵌套;

SpringMVC 的接口如果返回值为String类型时:(1)默认 Content-Type = text/plain,需要手动设置为 application/json;(2)统一封装后的返回值也必须为String,否则会报错 ClassCastException,所以需要将封装好的Result 转换成JSON字符串

测试

代码

package com.example.web.exception.controller;

import com.example.core.log.annotation.ApiLog;
import com.example.core.model.PageQuery;
import com.example.web.exception.query.UserQuery;
import com.example.web.model.vo.UserVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@RestController
@RequestMapping("exception")
@Tag(name = "异常统一处理")
public class ExceptionController {


    @ApiLog
    @GetMapping(path = "users")
    @Operation(summary = "查询用户列表", description = "测试:BindException。参数校验异常:Get请求,Query参数,以对象的形式接收。")
    public List<UserVO> listUsers(@Valid UserQuery userQuery, PageQuery pageQuery,
                                  HttpServletRequest request, HttpServletResponse response, HttpSession session) {
        log.info("查询用户列表。userQuery={},pageQuery={}", userQuery, pageQuery);

        String queryName = userQuery.getName();
        String queryPhone = userQuery.getMobilePhone();

        return listMockUsers().stream().filter(user -> {
            boolean isName = true;
            boolean isPhone = true;
            if (StringUtils.hasText(queryName)) {
                isName = user.getName().contains(queryName);
            }
            if (StringUtils.hasText(queryPhone)) {
                isPhone = user.getMobilePhone().contains(queryPhone);
            }
            return isName && isPhone;
        }).collect(Collectors.toList());
    }


    private List<UserVO> listMockUsers() {
        List<UserVO> list = new ArrayList<>();

        UserVO vo = new UserVO();
        vo.setId("1234567890123456789");
        vo.setName("张三");
        vo.setMobilePhone("18612345678");
        vo.setEmail("zhangsan@qq.com");
        vo.setBeginTime(new Date());
        vo.setEndTime(new Date());
        vo.setBeginDate(new Date());
        vo.setEndDate(new Date());
        list.add(vo);

        UserVO vo2 = new UserVO();
        vo2.setId("1234567890123456781");
        vo2.setName("李四");
        vo2.setMobilePhone("13412345678");
        vo2.setEmail("lisi@example.com");
        vo2.setBeginTime(new Date());
        vo2.setEndTime(new Date());
        vo2.setBeginDate(new Date());
        vo2.setEndDate(new Date());
        list.add(vo2);

        return list;
    }

}

效果

接口返回响应,统一封装(ResponseBodyAdvice + Result)(SpringBoot),Spring Boot,spring boot,后端,java文章来源地址https://www.toymoban.com/news/detail-714265.html

到了这里,关于接口返回响应,统一封装(ResponseBodyAdvice + Result)(SpringBoot)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • springboot统一拦截包装接口返回值

    1、作用 代替手动封装每个接口的返回值,否则每个有返回值的controller接口,都需要手动使用Resonse类包装返回结果 2、实现 方法返回值handler,需要实现接口HandlerMethodReturnValueHandler,并重写supportsReturnType和handleReturnValue方法。只有在supportsReturnType方法返回值为true的情况下才会

    2024年02月13日
    浏览(49)
  • SpringBoot第11讲:SpringBoot 如何统一接口封装

    本文是SpringBoot第11讲,在以SpringBoot开发Restful接口时统一返回,方便前端进行开发和封装,以及出现问题时给出响应编码和信息。 什么是 REST ?

    2024年02月09日
    浏览(32)
  • Springboot调整接口响应返回时长详解(解决响应超时问题)

    一、前言   当后端对于数据量较大的处理或是某些耗时的操作时,需要先对请求接口的请求进行响应。实际场景中会遇到 请求业务处理流程耗时较长 ,比如长查询,远程调用等,主线程会被一直占用会影响其他请求的响应,导致服务端性能下降。同时, 前端向服务端发送

    2024年01月15日
    浏览(26)
  • Springboot调整接口响应返回时长详解(解决响应超时问题)_springboot设置请求超时时间

    1、配置Http会话超时 可以通过两种方式为Spring Boot应用程序 配置HTTP会话超时 。 1.1 application.properties中配置会话超时 最简单的方法是在你的application.properties中加入参数 server.servlet.session.timeout 。 还要注意的是, Tomcat不允许你将超时时间设置得少于60秒 。 1.2 以程序方式配置会

    2024年04月27日
    浏览(49)
  • Response 接口统一返回实体类

    根据实际情况修改

    2024年02月16日
    浏览(22)
  • 如何设计 API 接口,实现统一格式返回?

    在移动互联网,分布式,微服务盛行的今天,现在项目绝大部分都采用的微服务框架,前分离分离方式,(题外话:前重新的工作分配越来越明确,现在的前端都称为大前端,技术栈以及生态圈都已经非常成熟;以前官员人员瞧不起前端人员,那现在高层人员要重新认识一下

    2024年02月12日
    浏览(32)
  • SpringBoot统一异常处理和统一返回格式

    上篇博客我们讲解了使用AOP来进行统一的用户登录判断,其实像这种功能统一且使用较多的地方,都可以用AOP来处理,除了统⼀的⽤户登录判断之外,AOP 还可以实现: 统⼀⽇志记录 统⼀⽅法执⾏时间统计 (在性能优化阶段,监控流量,接口的响应时间等甚至每个方法的响应

    2024年02月15日
    浏览(23)
  • 接口自动化测试:Requests统一请求封装(框架的封装)

    一、为什么要做统一请求封装? 1. 去除很多重复的、冗余的代码; 2.  异常处理和日志监控: 设置统一的公共参数、统一的文件处理、统一的异常处理、统一的日志监控、统一的用例断言等; 3. 跨py文件实现通过一个session自动管理有cookie关联的接口;               

    2024年01月24日
    浏览(38)
  • springboot全局统一返回处理

    项目中一般都会有规定好的接口返回格式,无论成功与失败,一般格式都是不变的,这样是为了方便前后端统一处理,今天就来说下前后端统一处理的较为优雅的方式; 一般而言都会有一个统一的返回类作为接口的返回数据的封装,例如: 然后我们通过此类作为返回参数的统一封装

    2024年02月13日
    浏览(27)
  • Springboot 设置统一的请求返回格式

    现在开发过程中主要采用前后端分离的方式进行开发测试,也就是前端封装请求,后端提供标准的API接口服务。一般现在json 格式受到开发者们的青睐,学习过程中我们可以设置接口的返回类型,那么怎么做到设置统一的返回格式呢?以下是在项目开发过程中一般的模式标准

    2024年02月13日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包