springboot统一接口返回数据的实现

一,没有异常的情况,正常返回数据

希望接口统一返回的数据格式如下:

{
  "status": 0,
  "msg": "成功",
  "data": null
}

和接口数据对应的bean

/**
 * 统一返回结果的实体
 * @param <T>
 */
public class Result<T> implements Serializable {

  private static final long serialVersionUID = 1L;

  /**
   * 错误码
   */
  private int status;

  /**
   * 提示消息
   */
  private String msg;

  /**
   * 返回的数据体
   */
  private T data;

  public int getStatus() {
    return status;
  }

  public void setStatus(int status) {
    this.status = status;
  }

  public String getMsg() {
    return msg;
  }

  public void setMsg(String msg) {
    this.msg = msg;
  }

  public T getData() {
    return data;
  }

  public void setData(T data) {
    this.data = data;
  }
}

操作Result实体的工具类

/**
 * 生成result的工具类,避免重复代码
 */
public class ResultUtils {

  /**
   * 成功时生成result的方法,有返回数据
   */
  public static <T> Result<T> success(T t){
    Result<T> result = new Result<>();
    result.setStatus(ResultEnum.SUCCESS.getCode());
    result.setMsg(ResultEnum.SUCCESS.getMsg());
    result.setData(t);
    return result;
  }

  /**
   * 成功时生成result的方法,无返回数据
   */
  public static <T> Result<T> success(){
    return success(null);
  }

  /**
   * 失败时生成result的方法
   */
  public static <T> Result<T> error(int status, String msg){
    Result<T> result = new Result<>();
    result.setStatus(status);
    result.setMsg(msg);
    return result;
  }
}

封装错误码和错误消息的枚举类

/**
 * 所有返回结果的枚举
 */
public enum ResultEnum {

  UNKNOWN_ERROR(-1, "未知错误"),
  SUCCESS(0, "成功"),
  BASIC_INFO_ID_IS_EMPTY(600, "基本信息中BasicInfoId为空"),
  BASIC_INFO_ADD_TO_DATABASE_FAILURE(601, "向数据库添加基本信息失败"),
  DETAILS_DATA_BASIC_INFO_ID_IS_EMPTY(602, "测试数据中BasicInfoId为空"),
  DETAILS_DATA_ADD_TO_DATABASE_FAILURE(603, "向数据库添加测试数据失败");

  ResultEnum(int code, String msg) {
    this.code = code;
    this.msg = msg;
  }

  private int code;

  private String msg;

  public int getCode() {
    return code;
  }

  public void setCode(int code) {
    this.code = code;
  }

  public String getMsg() {
    return msg;
  }

  public void setMsg(String msg) {
    this.msg = msg;
  }

  @Override
  public String toString() {
    return "ResultEnum{" +
        "code=" + code +
        ", msg='" + msg + '\'' +
        '}';
  }
}

统一封装返回结果的切面

之所以需要这个切面,是为了避免每个Controller方法中都要调用ResultUtils.success()。有了这个切面,Controller可以和原来一样正常返回对象,字符串,void,在切面里面将结果封装成Result实体,而不需要每个Controller方法都返回Result实体。

/**
 * 统一处理返回结果的切面,避免每个controller方法里面都要调用ResultUtils.success()这句话
 * 统一在这个切面里面调用
 */
@ControllerAdvice
public class MyResponseAdvice implements ResponseBodyAdvice<Object> {

  @Autowired
  private ObjectMapper objectMapper;

  /**
   * Whether this component supports the given controller method return type
   * and the selected {@code HttpMessageConverter} type.
   *
   * @param returnType  the return type
   * @param converterType the selected converter type
   * @return {@code true} if {@link #beforeBodyWrite} should be invoked;
   * {@code false} otherwise
   */
  @Override
  public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
    return true;
  }

  /**
   * Invoked after an {@code HttpMessageConverter} is selected and just before
   * its write method is invoked.
   *
   * @param body         the body to be written
   * @param returnType      the return type of the controller method
   * @param selectedContentType  the content type selected through content negotiation
   * @param selectedConverterType the converter type selected to write to the response
   * @param request        the current request
   * @param response       the current response
   * @return the body that was passed in or a modified (possibly new) instance
   */
  @Override
  public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
    if(body instanceof Result){ //发生异常之后,异常处理器里面返回的已经是Result了
      return body;
    }else if(body instanceof String){ //String属于特殊情况,需要单独处理,否则会报错
      try {
        return objectMapper.writeValueAsString(ResultUtils.success(body));
      } catch (JsonProcessingException e) {
        e.printStackTrace();
        return ResultUtils.error(ResultEnum.UNKNOWN_ERROR.getCode(), e.getMessage());
      }
    }
    return ResultUtils.success(body);
  }
}

二,有异常的情况下

service层为了自动回滚事务,会抛出一些自定义的RuntimeException。默认情况下,只有RuntimeException才会回滚事务。如果Controller里面直接处理service层抛出的异常,则Controller里面到处都是try catch块,代码会很难看。将异常集中在一个地方处理会好很多。

springboot中是通过@ControllerAdvice和@ExceptionHandler来完成统一异常处理的。这2个注解只能处理Controller和拦截器中抛出的异常,其他地方抛出的异常(比如Filter中抛出的异常),无法捕获。其他地方抛出的异常会转到/error的Controller方法来处理,默认是BasicErrorController来处理,为了能处理其他地方抛出的异常,我们会自定义ErrorController。

统一的异常处理类,处理Controller和拦截器抛出的异常

/**
 * 统一的异常处理类
 */
@ControllerAdvice
public class MyExceptionHandler {

  /**
   * 转发到/error,表示由BasicErrorController处理,
   * BasicErrorController是由springboot自动装配到容器中的
   */
  /*@ExceptionHandler(BasicInfoException.class)
  public String handleException(Exception ex, HttpServletRequest request){
    request.setAttribute("javax.servlet.error.status_code", 401);
    request.setAttribute("exMsg", ex.getMessage());
    return "forward:/error";
  }*/

  /**
   * 处理基本信息相关的异常
   */
  @ExceptionHandler(BasicInfoException.class)
  @ResponseBody
  public Result handleBasicInfoException(BasicInfoException ex){
    return ResultUtils.error(ex.getCode(), ex.getMessage());
  }

  /**
   * 处理测试数据相关的异常
   */
  @ExceptionHandler(DetailsDataException.class)
  @ResponseBody
  public Result handleDetailsDataException(DetailsDataException ex){
    return ResultUtils.error(ex.getCode(), ex.getMessage());
  }

  /**
   * 处理未知异常
   */
  @ExceptionHandler(Exception.class)
  @ResponseBody
  public Result handleUnKnowException(Exception ex){
    return ResultUtils.error(ResultEnum.UNKNOWN_ERROR.getCode(), ex.getMessage());
  }

}

自定义的异常类示例

public class BasicInfoException extends RuntimeException {

  private int code;

  public BasicInfoException(int code, String msg){
    super(msg);
    this.code = code;
  }

  public int getCode() {
    return code;
  }
}

处理其他地方抛出的异常(不是Controller和拦截器抛出的异常),自定义ErrorController

/**
 * 自定义ErrorController,处理其他地方抛出的异常(不是Controller和拦截器抛出的异常)
 */
@Controller
public class MyBasicErrorController extends AbstractErrorController {

  private Logger logger = LoggerFactory.getLogger(this.getClass());

  /**
   * 可以通过@Value获取到
   */
  @Value("${server.error.path}")
  private String myPath;

  private final ErrorProperties errorProperties;

  private ErrorAttributes mErrorAttributes;

  public MyBasicErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties) {
    super(errorAttributes);
    this.errorProperties = serverProperties.getError();
    this.mErrorAttributes = errorAttributes;
  }

  //@RequestMapping(value = "/error")
  @RequestMapping("${server.error.path}") //从properties文件中获取
  @ResponseBody
  public Result<Object> error(HttpServletRequest request) throws Throwable {
    logger.debug("myPath = " + myPath);

    //发生错误之后直接将异常抛出去,异常会到统一异常处理器中处理
    WebRequest webRequest = new ServletWebRequest(request);
    Throwable throwable = this.mErrorAttributes.getError(webRequest).getCause();
    throw throwable;
    /*UserException ex;
    if(throwable instanceof UserException){
      ex = (UserException) throwable;
      throw ex;
    }else{
      throw throwable;
    }*/
    /*HttpStatus status = getStatus(request);
    if (status == HttpStatus.NO_CONTENT) {
      return ResultUtils.error(status.value(), status.name());
    }
    Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
    return ResultUtils.error((Integer) body.get("status"), (String)body.get("message"));*/
  }

  /**
   * Determine if the stacktrace attribute should be included.
   * @param request the source request
   * @param produces the media type produced (or {@code MediaType.ALL})
   * @return if the stacktrace attribute should be included
   */
  private boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {
    ErrorProperties.IncludeStacktrace include = getErrorProperties().getIncludeStacktrace();
    if (include == ErrorProperties.IncludeStacktrace.ALWAYS) {
      return true;
    }
    if (include == ErrorProperties.IncludeStacktrace.ON_TRACE_PARAM) {
      return getTraceParameter(request);
    }
    return false;
  }

  /**
   * Provide access to the error properties.
   * @return the error properties
   */
  private ErrorProperties getErrorProperties() {
    return this.errorProperties;
  }

  /**
   * Returns the path of the error page.
   *
   * @return the error path
   */
  @Override
  public String getErrorPath() {
    return this.errorProperties.getPath();
  }
}

自定义ErrorController中错误处理的方法中,也可以直接将异常抛出,这样异常就会交给统一异常处理器进行处理。

 //@RequestMapping(value = "/error")
  @RequestMapping("${server.error.path}") //从properties文件中获取
  @ResponseBody
  public Result<Object> error(HttpServletRequest request) throws Throwable {
    logger.debug("myPath = " + myPath);

    //发生错误之后直接将异常抛出去,异常会到统一异常处理器中处理
    WebRequest webRequest = new ServletWebRequest(request);
    Throwable throwable = this.mErrorAttributes.getError(webRequest).getCause();
    UserException ex;
    if(throwable instanceof UserException){
      ex = (UserException) throwable;
      throw ex;
    }else{
      throw throwable;
    }
    /*HttpStatus status = getStatus(request);
    if (status == HttpStatus.NO_CONTENT) {
      return ResultUtils.error(status.value(), status.name());
    }
    Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
    return ResultUtils.error((Integer) body.get("status"), (String)body.get("message"));*/
  }

到此这篇关于springboot统一接口返回数据的实现的文章就介绍到这了,更多相关springboot统一接口返回数据内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • spring boot 统一JSON格式的接口返回结果的实现

    前后端分离的项目开发前,会提前规定好数据返回格式,本文以JSON为例. 第一步,定义好JavaBean. package com.yclouds.myhelper.web.response; import com.fasterxml.jackson.annotation.JsonIgnore; import com.yclouds.myhelper.web.error.code.BaseEnumError; import java.io.Serializable; import lombok.D

  • SpringBoot接口加密解密统一处理

    我们与客户端的接口交互中,为了更高的安全性,我们可能需要对接口加密(请求参数加密,服务端解密).返回信息加密(服务端加密,客户端解密),但是也不是所有的接口都这样,有些接口可能不需要,我们可以使用注解来轻松达到此要求. 将接口参数的加密解密和返回信息的加密解密分开,分别定义注解,利用Controller的ControllerAdvice来拦截所有的请求,在其中判断是否需要加密解密,即可达到要求. 使用方法:使用 DecryptRequest 和 EncryptResponse 注解即可,可以放在

  • springboot统一接口返回数据的实现

    一,没有异常的情况,正常返回数据 希望接口统一返回的数据格式如下: { "status": 0, "msg": "成功", "data": null } 和接口数据对应的bean /** * 统一返回结果的实体 * @param <T> */ public class Result<T> implements Serializable { private static final long serial

  • SpringBoot统一接口返回及全局异常处理高级用法

    前言 现在大多数公司项目框架,基本都是属于前后端分离模式,这种模式会涉及到一个前后端对接问题,无论是对前端或者是后台服务,维护一套完善且规范的接口是非常有必要的,这样不仅能够提高对接效率,也可以让我的代码看起来更加简洁优雅. 修改前后最大的区别是我们不用在每个接口单独捕获异常,也不用在每个接口都要组装一遍返回参数,可以参考下面这张对比图: 一.SpringBoot不使用统一返回格式 默认情况下,SpringBoot会有如下三种返回情况. 1.1 使用字符串返回 @GetMapping("/get

  • SpringBoot中如何统一接口返回与全局异常处理详解

    目录 背景 统一接口返回 定义API返回码枚举类 定义正常响应的API统一返回体 定义异常响应的API统一返回体 编写包装返回结果的自定义注解 定义返回结果拦截器 WebMvc配置类拦截器注册者添加返回结果拦截器 编写响应体处理器 接口调用 测试结果 全局异常处理 编写自定义异常基类 编写自定义业务异常类 定义全局异常处理类 接口调用 测试结果 总结 背景 在分布式.微服务盛行的今天,绝大部分项目都采用的微服务框架,前后端分离方式.前端和后端进行交互,前端按照约定请求URL路径,并传入相关参数,

  • 关于springboot的接口返回值统一标准格式

    目录 一.目标 二.为什么要对springboot的接口返回值统一标准格式? 第一种格式:response为String 第二种格式:response为Objct 第三种格式:response为void 第四种格式:response为异常 三.定义response的标准格式 四.初级程序员对response代码封装 步骤1:把标准格式转换为代码 步骤2:把状态码存在枚举类里面 步骤3:加一个体验类 五.高级程序员对response代码封装 步骤1:采用ResponseBodyAdvice技术来实

  • SpringBoot 统一请求返回的实现

    目前我们的返回是直接把实体类扔给请求方,这样很不友好,一方面没有统一的格式,二来请求方不知道请求成功与否,没有一个可以判断的东西,也没有说明性的返回. 本篇就来为所有接口提供一个统一的友好返回. 确定返回结构 首先,我们先确定好我们接口返回的格式是什么样的,然后再一步一步实现下面的. { "code": 200, "msg": "ok", "data": "" } code 字段表示状态码,调用方根据该码来

  • SpringBoot 定制化返回数据的实现示例

    此时我们的返回结构如下: { "code": 200, "msg": "ok", "data": { "id": 1, "username": "steve", "secretKey": "xxx", "expiredAt": null, "createdAt": "2020-0

  • SpringBoot 统一公共返回类的实现

    目录 项目结构 统一返回类使用 本文记录下如何在工程中,配置后台的统一公共返回类,这样做目的是为了统一返回信息,方便项目进行管理.使用技术:SpringBoot+Swagger+Lombok 项目结构 具体过程如下,在response文件夹下分别建立 CustomizeResultCode 接口,枚举类ResultCode 实现CustomizeResultCode 接口和Result类: 1 配置自定义返回类接口,该接口中包含 错误状态码和错误信息 public interface Custo

  • 详解SpringBoot 统一后端返回格式的方法

    目录 为什么要对SpringBoot返回统一的标准格式 定义返回标准格式 定义返回对象 定义状态码 统一返回格式 高级实现方式 接口异常问题 SpringBoot为什么需要全局异常处理器 如何实现全局异常处理器 体验效果 全局异常接入返回的标准格式 首先我们来看看为什么要返回统一的标准格式? 为什么要对SpringBoot返回统一的标准格式 在默认情况下,SpringBoot的返回格式常见的有三种: 第一种:返回 String @GetMapping("/hello") public

  • SpringBoot统一api返回风格的实现

    学过上一章的人也许发现问题了,接口返回的格式基本都是固定的,例如 { "code": 200, "msg": "", "data": "Object ....", } 而我们的登陆却返回 密码必须传!等等返回值 显然我们的返回格式不符合常规开发,因为前台并不知道这是错误信息还是正确返回信息! 所以我们简单封装一个返回类,代码如下 添加base包,新建BaseResult类 import lombok.Dat

  • Springboot+AOP实现返回数据提示语国际化的示例代码

    前言 本篇内容: 提示语的国际化返回,自定义多语言. 本文使用aop方式,拦截接口返回的数据,进行转换. 正文 先看这次示例教学的项目 目录结构: (当然resource里面的i18n文件夹和三个properties文件也是要我们自己建的,但是 那个Resource Bundle 不用管,这个在yml加上对应配置项自动生成的. 不清楚的继续往下看教学就好) 开始敲(CV)代码: pom.xml 依赖: <dependencies> <dependency> <groupId&

随机推荐