SpringBoot项目接口返回类型强制设定
yangdi 2024-03-01 springboot
# 背景:强制要求开发者编写接口返回响应结果统一
# 知识点
自定义异常
全局异常处理器
ResponseBodyAdvice
响应结果封装
# 自定义异常
package com.example.demo.exception;
import com.example.demo.utils.result.IResultCode;
import com.example.demo.utils.result.ResultCode;
import lombok.Getter;
/**
* @author YangDi
*/
public class ServiceException extends RuntimeException{
private static final long serialVersionUID = 1L;
@Getter
private final IResultCode resultCode;
public ServiceException(String message) {
super(message);
this.resultCode = ResultCode.FAILURE;
}
public ServiceException(IResultCode resultCode) {
super(resultCode.getMessage());
this.resultCode = resultCode;
}
public ServiceException(IResultCode resultCode, Throwable cause) {
super(cause);
this.resultCode = resultCode;
}
}
# 全局异常处理器
package com.example.demo.exception;
import com.example.demo.utils.result.ResultEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* @author YangDi
* @since 2024/3/17
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(ServiceException.class)
public ResultEntity handleServiceException(ServiceException e) {
log.error("ServiceException: ", e);
return ResultEntity.fail(e.getResultCode(),e.getMessage());
}
}
@RestControllerAdvice整合了 @ControllerAdvice 和 @ResponseBody 两个注解的功能
@ExceptionHandler方法会捕获并处理由控制器内其他方法抛出的特定类型异常
# ResponseBodyAdvice
package com.example.demo.interceptor;
import com.example.demo.exception.ServiceException;
import com.example.demo.utils.result.R;
import com.example.demo.utils.result.ResultCode;
import org.springframework.core.MethodParameter;
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.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* @author YangDi
*/
@ControllerAdvice
public class ResultAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return !R.class.isAssignableFrom(returnType.getParameterType());
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
throw new ServiceException(ResultCode.MSG_NOT_READABLE);
}
}
@ControllerAdvice用于定义全局的控制器增强逻辑 ResponseBodyAdvice用于在方法执行完成后对HTTP响应体进行修改或过滤的全局处理 beforeBodyWrite: 这个方法会在控制器方法执行完成后,将结果转换为HTTP响应体之前被调用。 supports: 这个方法用于判断当前的处理逻辑是否适用于给定的方法参数和返回类型。
# 响应结果封装
package com.example.demo.utils.result;
import com.example.demo.utils.constant.FeatherConstant;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import java.io.Serializable;
import java.util.Optional;
/**
* @author YangDi
*/
@Getter
@Setter
@ToString
@NoArgsConstructor
public class ResultEntity<T> implements Serializable {
private static final long serialVersionUID = 1L;
private int code;
private boolean success;
private T data;
private String msg;
private ResultEntity(IResultCode resultCode) {
this(resultCode, null, resultCode.getMessage());
}
private ResultEntity(IResultCode resultCode, String msg) {
this(resultCode, null, msg);
}
private ResultEntity(IResultCode resultCode, T data) {
this(resultCode, data, resultCode.getMessage());
}
private ResultEntity(IResultCode resultCode, T data, String msg) {
this(resultCode.getCode(), data, msg);
}
private ResultEntity(int code, T data, String msg) {
this.code = code;
this.data = data;
this.msg = msg;
this.success = ResultCode.SUCCESS.code == code;
}
public static boolean isSuccess(@Nullable ResultEntity<?> result) {
return Optional.ofNullable(result)
.map(x -> ObjectUtils.nullSafeEquals(ResultCode.SUCCESS.code, x.code))
.orElse(Boolean.FALSE);
}
public static boolean isNotSuccess(@Nullable ResultEntity<?> result) {
return !ResultEntity.isSuccess(result);
}
public static <T> ResultEntity<T> data(T data) {
return data(data, FeatherConstant.DEFAULT_SUCCESS_MESSAGE);
}
public static <T> ResultEntity<T> data(T data, String msg) {
return data(HttpServletResponse.SC_OK, data, msg);
}
public static <T> ResultEntity<T> data(int code, T data, String msg) {
return new ResultEntity<>(code, data, data == null ? FeatherConstant.DEFAULT_NULL_MESSAGE : msg);
}
public static <T> ResultEntity<T> success(String msg) {
return new ResultEntity<>(ResultCode.SUCCESS, msg);
}
public static <T> ResultEntity<T> success(IResultCode resultCode) {
return new ResultEntity<>(resultCode);
}
public static <T> ResultEntity<T> success(IResultCode resultCode, String msg) {
return new ResultEntity<>(resultCode, msg);
}
public static <T> ResultEntity<T> fail(String msg) {
return new ResultEntity<>(ResultCode.FAILURE, msg);
}
public static <T> ResultEntity<T> fail(int code, String msg) {
return new ResultEntity<>(code, null, msg);
}
public static <T> ResultEntity<T> fail(IResultCode resultCode) {
return new ResultEntity<>(resultCode);
}
public static <T> ResultEntity<T> fail(IResultCode resultCode, String msg) {
return new ResultEntity<>(resultCode, msg);
}
public static <T> ResultEntity<T> status(boolean flag) {
return flag ? success(FeatherConstant.DEFAULT_SUCCESS_MESSAGE) : fail(FeatherConstant.DEFAULT_FAILURE_MESSAGE);
}
}
package com.example.demo.utils.result;
import java.io.Serializable;
/**
* @author YangDi
*/
public interface IResultCode extends Serializable {
String getMessage();
int getCode();
}
package com.example.demo.utils.result;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author YangDi
*/
@Getter
@AllArgsConstructor
public enum ResultCode implements IResultCode{
SUCCESS(HttpServletResponse.SC_OK, "操作成功"),
FAILURE(HttpServletResponse.SC_BAD_REQUEST, "业务异常"),
MSG_NOT_READABLE(HttpServletResponse.SC_BAD_REQUEST, "消息不能读取"),
;
final int code;
final String message;
}
package com.example.demo.utils.constant;
/**
* @author YangDi
*/
public interface FeatherConstant {
String DEFAULT_NULL_MESSAGE = "暂无承载数据";
String DEFAULT_SUCCESS_MESSAGE = "操作成功";
String DEFAULT_FAILURE_MESSAGE = "操作失败";
}
package com.example.demo.controller;
import com.example.demo.utils.result.ResultEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author YangDi
*/
@RestController
@RequestMapping("/demo01")
public class Demo01Controller {
@GetMapping("/queryDemo01")
public String queryDemo01() {
return "demo01";
}
@GetMapping("/queryDemo02")
public ResultEntity<String> queryDemo02() {
return ResultEntity.data("queryDemo02");
}
}
# 效果:
访问:http://localhost:8080/feather/demo01/queryDemo01,返回:{"code":400,"success":false,"data":null,"msg":"消息不能读取"}
访问:http://localhost:8080/feather/demo01/queryDemo02,返回:{"code":200,"success":true,"data":"queryDemo02","msg":"操作成功"}