在项目开发过程中,很难避免返回各种各样的结果,就会导致前端渲染时造成混乱,怎么统一返回呢?
编写R.java Object
代码语言:javascript复制/**
* @Author Joker DJ
* @Date 2021/8/7 20:58
* @Version 1.0
*/
public class R {
/**
*标识返回状态
*/
private Integer code;
/**
* 标识返回内容
*/
private Object data;
/**
* 标识返回消息
*/
private String message;
/**
* 成功返回
* @param data
* @return
*/
public static R ok(Object data){
return new R(RHttpStatusEnum.SUCCESS.getCode(),data,RHttpStatusEnum.SUCCESS.getMessage());
}
/**
* 成功返回
* @param data
* @return
*/
public static R ok(Object data,String message){
return new R(RHttpStatusEnum.SUCCESS.getCode(),data,message);
}
/**
* 失败返回
* @param rHttpStatusEnum
* @return
*/
public static R error(RHttpStatusEnum rHttpStatusEnum){
return new R(rHttpStatusEnum.getCode(),null,rHttpStatusEnum.getMessage());
}
public static R error(Integer code,String message){
R r = new R();
r.code(code);
r.data(null);
r.message(message);
return r;
}
public R() {
}
public R(Integer code, Object data, String message) {
this.code = code;
this.data = data;
this.message = message;
}
public Integer getCode() {
return code;
}
public R code(Integer code) {
this.code = code;
return this;
}
public Object getData() {
return data;
}
public R data(Object data) {
this.data = data;
return this;
}
public String getMessage() {
return message;
}
public R message(String message) {
this.message = message;
return this;
}
}
编写R.java 泛型
代码语言:javascript复制package com.dj.rvo.Vo;
/**
* @Author Joker DJ
* @Date 2021/8/7 20:58
* @Version 1.0
*/
public class RT<T> {
/**
* 标识返回状态
*/
private Integer code;
/**
* 标识返回内容
*/
private T data;
/**
* 标识返回消息
*/
private String message;
/**
* 成功返回
*
* @param data
* @return
*/
public static <T> RT ok(T data) {
return new RT(RHttpStatusEnum.SUCCESS.getCode(), data, RHttpStatusEnum.SUCCESS.getMessage());
}
/**
* 成功返回
*
* @param data
* @return
*/
public static <T> RT ok(T data, String message) {
return new RT(RHttpStatusEnum.SUCCESS.getCode(), data, message);
}
/**
* 失败返回
*
* @param rHttpStatusEnum
* @return
*/
public static RT error(RHttpStatusEnum rHttpStatusEnum) {
return new RT(rHttpStatusEnum.getCode(), null, rHttpStatusEnum.getMessage());
}
public static RT error(Integer code,String message){
RT r = new RT();
r.code(code);
r.data(null);
r.message(message);
return r;
}
public RT() {
}
public RT(Integer code, T data, String message) {
this.code = code;
this.data = data;
this.message = message;
}
public Integer getCode() {
return code;
}
public RT code(Integer code) {
this.code = code;
return this;
}
public Object getData() {
return data;
}
public RT data(T data) {
this.data = data;
return this;
}
public String getMessage() {
return message;
}
public RT message(String message) {
this.message = message;
return this;
}
}
编写返回枚举 RHttpStatusEnum
代码语言:javascript复制package com.dj.rvo.Vo;
/**
* @Author Joker DJ
* @Date 2021/8/7 21:16
* @Version 1.0
*/
public enum RHttpStatusEnum {
SUCCESS(200,"success"),
HTTP_NOT_FOUND(404,"未找到相关内容"),
SERVER_ERROR( 500, "服务器忙,请稍后在试");
private final int code;
private final String message;
RHttpStatusEnum(Integer code,String message){
this.code=code;
this.message=message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
测试
代码语言:javascript复制/**
* 返回错误信息
* @return
*/
public R test1(){
return R.error(RHttpStatusEnum.HTTP_NOT_FOUND);
}
/**
* 返回成功信息
* @return
*/
public R test2() {
return R.ok("成功");
}
public R test3() {
return R.ok("数据","信息");
}
问题:如果都返回R类 太强制了;通过通知来封装R类
编写 ResultResponseHandler
代码语言:javascript复制package com.dj.rvo.Handler;
import com.dj.rvo.Utils.JsonUtil;
import com.dj.rvo.Vo.R;
import com.fasterxml.jackson.databind.ObjectMapper;
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 Joker DJ
* @Date 2021/8/7 21:53
* @Version 1.0
*/
@ControllerAdvice(basePackages = "com.dj.rvo.Controller")// 绑定的Controller包的权限定名 例如:com.dj.Controller
public class ResultResponseHandler implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
// 对请求的结果在这里统一返回和处理
if (o instanceof ErrorHandler) {
// 1、如果返回的结果是一个异常的结果,就把异常返回的结构数据倒腾到R.fail里面即可
ErrorHandler errorHandler = (ErrorHandler) o;
return R.error(errorHandler.getStatus(), errorHandler.getMessage());
} else if (o instanceof String) {
try {
// 2、因为springmvc数据转换器对String是有特殊处理 StringHttpMessageConverter
ObjectMapper objectMapper = new ObjectMapper();
R r = R.ok(o);
return objectMapper.writeValueAsString(r);
}catch ( Exception ex){
ex.printStackTrace();
}
}
return R.ok(o);
}
}
编写自定义异常返回ErrorHandler
代码语言:javascript复制package com.dj.rvo.Handler;
import com.dj.rvo.Vo.RHttpStatusEnum;
/**
*
* 功能描述: 异常返回
*
* @param:
* @return:
* @auther: Joker
* @date: 2021/8/7 22:25
*/
public class ErrorHandler {
// ErrorHandler === R 答案:不想破坏R类。
// 异常的状态码,从枚举中获得
private Integer status;
// 异常的消息,写用户看得懂的异常,从枚举中得到
private String message;
// 异常的名字
private String exception;
/**
* 对异常处理进行统一封装
*
* @param resultCodeEnum 异常枚举
* @param throwable 出现异常
* @param message 异常的消息 null /by zero
* @return
*/
public static ErrorHandler fail(RHttpStatusEnum resultCodeEnum, Throwable throwable, String message) {
ErrorHandler errorHandler = ErrorHandler.fail(resultCodeEnum, throwable);
errorHandler.setMessage(message);
return errorHandler;
}
/**
* 对异常枚举进行封装
*
* @param resultCodeEnum
* @param throwable
* @return
*/
public static ErrorHandler fail(RHttpStatusEnum resultCodeEnum, Throwable throwable) {
ErrorHandler errorHandler = new ErrorHandler();
errorHandler.setMessage(resultCodeEnum.getMessage());
errorHandler.setStatus(resultCodeEnum.getCode());
errorHandler.setException(throwable.getClass().getName());
return errorHandler;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getException() {
return exception;
}
public void setException(String exception) {
this.exception = exception;
}
}
编写异常通知类 GlobalRestExceptionHandler
代码语言:javascript复制package com.dj.rvo.Handler;
import com.dj.rvo.Vo.RHttpStatusEnum;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* 功能描述: 统一异常处理
*
* @param:
* @return:
* @auther: Joker
* @date: 2021/8/7 22:30
*/
@RestControllerAdvice
public class GlobalRestExceptionHandler {
/**
* 对服务器端出现500异常进行统一处理
* 缺点:不明确
* 场景:
*/
@ExceptionHandler(Throwable.class)
public ErrorHandler makeExcepton(Throwable e, HttpServletRequest request) {
ErrorHandler errorHandler = ErrorHandler.fail(RHttpStatusEnum.SERVER_ERROR, e);
return errorHandler;
}
/**
* 对服务器端出现500异常进行统一处理
* 缺点:明确异常信息
*/
@ExceptionHandler(BusinessException.class)
public ErrorHandler makeOrderException(BusinessException businessException, HttpServletRequest request) {
ErrorHandler errorHandler = new ErrorHandler();
errorHandler.setMessage(businessException.getMessage());
errorHandler.setStatus(businessException.getCode());
return errorHandler;
}
/**
* 对验证的统一异常进行统一处理
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ErrorHandler handlerValiator(MethodArgumentNotValidException e, HttpServletRequest request) throws JsonProcessingException {
// 1: 从MethodArgumentNotValidException提取验证失败的所有的信息。返回一个List<FieldError>
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
// 2: 把fieldErrors中,需要的部分提出出来进行返回
List<Map<String, String>> mapList = toValidatorMsg(fieldErrors);
// 3: 把需要的异常信息转换成json进行返回
ObjectMapper objectMapper = new ObjectMapper();
String mapJson = objectMapper.writeValueAsString(mapList);
ErrorHandler errorHandler = ErrorHandler.fail(RHttpStatusEnum.SERVER_ERROR, e, mapJson);
return errorHandler;
}
/**
* 对验证异常进行统一处理提取需要的部分
*
* @param fieldErrorList
* @return
*/
private List<Map<String, String>> toValidatorMsg(List<FieldError> fieldErrorList) {
List<Map<String, String>> mapList = new ArrayList<>();
// 循环提取
for (FieldError fieldError : fieldErrorList) {
Map<String, String> map = new HashMap<>();
// 获取验证失败的属性
map.put("field", fieldError.getField());
// 获取验证失败的的提示信息
map.put("msg", fieldError.getDefaultMessage());
mapList.add(map);
}
return mapList;
}
}
自定义异常 BusinessException
代码语言:javascript复制package com.dj.rvo.Handler;
import com.dj.rvo.Vo.RHttpStatusEnum;
/**
*
* 功能描述:
*
* @param:
* @return:
* @auther: Joker
* @date: 2021/8/7 22:47
*/
public class BusinessException extends RuntimeException {
private Integer code;
private String message;
public BusinessException(RHttpStatusEnum resultCodeEnum) {
this.code = resultCodeEnum.getCode();
this.message = resultCodeEnum.getMessage();
}
public BusinessException(Integer code, String message) {
this.code = code;
this.message = message;
}
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;
}
}
测试
代码语言:javascript复制/**
* @Author Joker DJ
* @Date 2021/8/7 22:45
* @Version 1.0
*/
@RestController
public class Test {
@RequestMapping("/test1")
public Map<String,Object> test1(){
Map<String, Object> hashMap = new HashMap<>();
hashMap.put("username","JokerDJ");
return hashMap;
}
@RequestMapping("/test2")
public String test2(){
boolean flag = false;
System.out.println(1/0);
return "成功";
}
@RequestMapping("/test3")
public String test3(){
boolean flag = false;
if(!flag){
throw new BusinessException(500,"系统异常");
}
return "成功";
}
}
相关工具
代码语言:javascript复制package com.dj.rvo.Utils;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.type.TypeReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import java.text.SimpleDateFormat;
/**
* by mofeng
* <dependency>
* <groupId>com.fasterxml.jackson.dataformat</groupId>
* <artifactId>jackson-dataformat-avro</artifactId>
* </dependency>
*
* <dependency>
* <groupId>org.apache.commons</groupId>
* <artifactId>commons-lang3</artifactId>
* <version>3.6</version>
* </dependency>
*/
public class JsonUtil {
private static ObjectMapper objectMapper = new ObjectMapper();
private static Logger log = LoggerFactory.getLogger(JsonUtil.class);
static {
// 对象的所有字段全部列入
objectMapper.setSerializationInclusion(Inclusion.ALWAYS);
// 取消默认转换timestamps形式
objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
// 忽略空Bean转json的错误
objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
// 所有的日期格式都统一为以下的样式,即yyyy-MM-dd HH:mm:ss
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// 忽略 在json字符串中存在,但是在java对象中不存在对应属性的情况。防止错误
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 精度的转换问题
objectMapper.configure(DeserializationConfig.Feature.USE_BIG_DECIMAL_FOR_FLOATS, true);
objectMapper.configure(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
}
public static <T> String obj2String(T obj) {
if (obj == null) {
return null;
}
try {
return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
} catch (Exception e) {
log.warn("Parse Object to String error", e);
return null;
}
}
public static <T> String obj2StringPretty(T obj) {
if (obj == null) {
return null;
}
try {
return obj instanceof String ? (String) obj
: objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
} catch (Exception e) {
log.warn("Parse Object to String error", e);
return null;
}
}
public static <T> T string2Obj(String str, Class<T> clazz) {
if (StringUtils.isEmpty(str) || clazz == null) {
return null;
}
try {
return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz);
} catch (Exception e) {
log.warn("Parse String to Object error", e);
return null;
}
}
public static <T> T string2Obj(String str, TypeReference<T> typeReference) {
if (StringUtils.isEmpty(str) || typeReference == null) {
return null;
}
try {
return (T) (typeReference.getType().equals(String.class) ? str
: objectMapper.readValue(str, typeReference));
} catch (Exception e) {
log.warn("Parse String to Object error", e);
return null;
}
}
public static <T> T string2Obj(String str, Class<?> collectionClass, Class<?>... elementClasses) {
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
try {
return objectMapper.readValue(str, javaType);
} catch (Exception e) {
log.warn("Parse String to Object error", e);
return null;
}
}
public static void main(String[] args) {
//String json = "{"name":"Geely","color":"blue","id":1274670369972846594}";
/*
* User user = new User(); user.setId(2);
* user.setAccount("geely@happymmall.com"); user.setCreateTime(new Date());
* String userJsonPretty = JsonUtil.obj2StringPretty(user);
* log.info("userJson:{}",userJsonPretty);
*
*
* User user2 = JsonUtil.string2Obj(userJsonPretty, User.class);
* System.out.println(user2);
*/
}
}