springboot统一返回消息体
对于已知错误与未知错误,可以对未知错误进行钉钉机器人或者微信机器人的发送,及时的找到错误的地方,进行捕捉,这样错误就会越来越完善
定义正常返回对象、异常返回对象、返回状态码
正常返回对象
import java.io.Serializable;
public class Result implements Serializable {
private Integer code;
private String message;
private Object data;
public static Result success() {
Result result = new Result();
result.setResultCode(ResultCode.SUCCESS);
return result;
}
public static Result success(Object data) {
Result result = new Result();
result.setResultCode(ResultCode.SUCCESS);
result.setData(data);
return result;
}
public static Result failure(ResultCode resultCode) {
Result result = new Result();
result.setResultCode(resultCode);
return result;
}
public static Result failure(ResultCode resultCode, Object data) {
Result result = new Result();
result.setResultCode(resultCode);
result.setData(data);
return result;
}
public static Result failure(Integer code, String message, Object data) {
Result result = new Result();
result.setCode(code);
result.setMessage(message);
result.setData(data);
return result;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public void setResultCode(ResultCode resultCode) {
this.code = resultCode.code();
this.message = resultCode.message();
}
}
错误返回对象
public class ErrorResult extends RuntimeException {
private ResultCode resultCode;
private Object errors;
public ErrorResult(ResultCode resultCode, Object errors) {
this.resultCode = resultCode;
this.errors = errors;
}
public ResultCode getResultCode() {
return resultCode;
}
public void setResultCode(ResultCode resultCode) {
this.resultCode = resultCode;
}
public Object getErrors() {
return errors;
}
public void setErrors(Object errors) {
this.errors = errors;
}
}
返回状态码
public enum ResultCode {
// todo 成功状态码
SUCCESS(200, "成功"),
// todo 参数错误
PARAM_IS_NULL(1001, "参数为空"),
// todo 用户错误
USER_NOT_LOGIN(2001, "用户未登录"),
// todo 其他错误
ERROR(9999, "其他异常");
private Integer code;
private String message;
ResultCode(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer code() {
return this.code;
}
public String message() {
return this.message;
}
}
定义全局统一返回体注解类、自定义返回体、区分已知异常与未知异常、拦截器添加标记
全局统一返回体注解类
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
public @interface ResponseResult {
}
自定义返回体
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.servlet.http.HttpServletRequest;
@ControllerAdvice
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
private static final Logger log = LoggerFactory.getLogger(ResponseResultHandler.class);
// 标记名称
public static final String RESPONSE_RESULT = "RESPONSE-RESULT";
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = sra.getRequest();
ResponseResult responseResult = (ResponseResult) request.getAttribute(RESPONSE_RESULT);
return responseResult == null ? false : true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
try {
// todo 异常返回处理
if (body instanceof ErrorResult) {
ErrorResult errorResult = (ErrorResult) body;
return Result.failure(errorResult.getResultCode(), errorResult.getErrors());
}
} catch (Exception e) {
log.error("request uri path: {}, format response body error", request.getURI().getPath(), e);
}
// todo 成功返回处理
return Result.success(body);
}
}
区分已知异常与未知异常
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理自定义异常
*
*/
@ExceptionHandler(value = ErrorResult.class)
@ResponseBody
public ErrorResult bizExceptionHandler(ErrorResult result) {
return result;
}
/**
* 处理其他异常
*
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ErrorResult exceptionHandler(Exception e) {
return new ErrorResult(ResultCode.ERROR, e.getMessage());
}
}
拦截器添加标记
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
@Component
public class LoginInterceptor implements HandlerInterceptor {
private static final Logger log = LoggerFactory.getLogger(LoginInterceptor.class);
// 标记名称
public static final String RESPONSE_RESULT = "RESPONSE-RESULT";
@Autowired
private UserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// todo 添加标记
if (handler instanceof HandlerMethod) {
final HandlerMethod handlerMethod = (HandlerMethod) handler;
final Class<?> classs = handlerMethod.getBeanType();
final Method method = handlerMethod.getMethod();
if (classs.isAnnotationPresent(ResponseResult.class)) {
request.setAttribute(RESPONSE_RESULT, classs.getAnnotation(ResponseResult.class));
} else if (method.isAnnotationPresent(ResponseResult.class)) {
request.setAttribute(RESPONSE_RESULT, method.getAnnotation(ResponseResult.class));
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
新建几个接口测试一下
测试conreoller
import com.zyh.budget.config.ErrorResult;
import com.zyh.budget.config.ResponseResult;
import com.zyh.budget.config.ResultCode;
import com.zyh.budget.entity.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description //TODO 全局统一返回体测试控制器
* @Author Zhong Yanghao
* @Date 2021/03/16 17:00
*/
@RestController
@ResponseResult
@RequestMapping("global")
public class GlobalHandlerController {
@GetMapping("test")
public User test() {
User user = new User();
user.setName("test");
user.setPhone("11111111");
return user;
}
@GetMapping("test2")
public String test2() {
return "user";
}
@GetMapping("err")
public String err() throws Exception {
throw new Exception("错误", new Throwable("111"));
}
@GetMapping("err2")
public String err2() {
throw new ErrorResult(ResultCode.USER_NOT_LOGGEN_IN, "错误");
}
}
问题
返回String类型会导致无法转换的问题
方法一:在自定义返回体中如果是String类型,单独返回处理,具体修改如下,解决String类型转换异常处
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.servlet.http.HttpServletRequest;
@ControllerAdvice
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
private static final Logger log = LoggerFactory.getLogger(ResponseResultHandler.class);
// 标记名称
public static final String RESPONSE_RESULT = "RESPONSE-RESULT";
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = sra.getRequest();
ResponseResult responseResult = (ResponseResult) request.getAttribute(RESPONSE_RESULT);
return responseResult == null ? false : true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
try {
// todo 异常返回处理
if (body instanceof ErrorResult) {
ErrorResult errorResult = (ErrorResult) body;
return Result.failure(errorResult.getResultCode(), errorResult.getErrors());
// 解决String类型转换异常
} else if (body instanceof String) {
// 返回String类型时
return objectMapper.writeValueAsString(Result.success(body));
}
} catch (Exception e) {
log.error("request uri path: {}, format response body error", request.getURI().getPath(), e);
}
// todo 成功返回处理
return Result.success(body);
}
}
方法二:在webconfig中处理 Object 类型的 HttpMessageConverter 放得靠前一些,解决类型转换异常
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@Configuration
public class WebConfigurer implements WebMvcConfigurer {
/**
* 处理 Object 类型的 HttpMessageConverter 放得靠前一些,解决类型转换异常
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0, new MappingJackson2HttpMessageConverter());
}
}