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统一接口返回数据内容请搜索随便开发网以前的文章或继续浏览下面的相关文章希望大家以后多多支持随便开发网!

原文链接:https://blog.csdn.net/liangshui999/article/details/105366367

版权声明:本站所有内容均由互联网收集整理、网友上传,并且以计算机技术研究交流为目的,仅供大家参考、学习,不存在任何商业目的与商业用途. 若您需要商业运营或用于其他商业活动,请您购买正版授权并合法使用。 我们不承担任何技术及版权问题,且不对任何资源负法律责任。

分享到: