在项目开发过程中,很难避免返回各种各样的结果,就会导致前端渲染时造成混乱,怎么统一返回呢?
编写R.java Object
/*** @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;}}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
编写R.java 泛型
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;}}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
编写返回枚举 RHttpStatusEnum
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;}}123456789101112131415161718192021222324252627282930
测试
/*** 返回错误信息* @return*/public R test1(){return R.error(RHttpStatusEnum.HTTP_NOT_FOUND);}/*** 返回成功信息* @return*/public R test2() {return R.ok("成功");}public R test3() {return R.ok("数据","信息");}123456789101112131415161718
问题:如果都返回R类 太强制了;通过通知来封装R类
编写 ResultResponseHandler
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.Controllerpublic class ResultResponseHandler implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {return true;}@Overridepublic 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是有特殊处理 StringHttpMessageConverterObjectMapper objectMapper = new ObjectMapper();R r = R.ok(o);return objectMapper.writeValueAsString(r);}catch ( Exception ex){ex.printStackTrace();}}return R.ok(o);}}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
编写自定义异常返回ErrorHandler
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;}}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
编写异常通知类 GlobalRestExceptionHandler
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*/@RestControllerAdvicepublic 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;}}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
自定义异常 BusinessException
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;}}1234567891011121314151617181920212223242526272829303132333435363738394041
测试
/*** @Author Joker DJ* @Date 2021/8/7 22:45* @Version 1.0*/@RestControllerpublic 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 "成功";}}1234567891011121314151617181920212223242526272829303132
相关工具
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:ssobjectMapper.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);*/}}
分界
为什么使用
项目中我们会将响应封装成JSON返回,一般我们会将所有接口的数据格式统一, 使前端对数据的操作更一致、轻松。 一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数 据就可以。但是一般会包含状态码、返回消息、数据这几部分内容
一. 统一返回数据格式
{"success": 布尔, //响应是否成功"code": 数字, //响应码"message": 字符串, //返回消息"data": HashMap //返回数据,放在键值对中}123456
二 . 创建统一结果返回类
- 抽取出来作为公共部分
1. 创建公共部分模块Common
2. 创建接口定义返回码 也可使用枚举 也可以不定义,直接写入
public interface ResultCode {public static Integer SUCCESS = 20000;public static Integer ERROR = 20001;}1234
3. 创建结果类
//统一返回结果的类@Datapublic class R {@ApiModelProperty(value = "是否成功")private Boolean success;@ApiModelProperty(value = "返回码")private Integer code;@ApiModelProperty(value = "返回消息")private String message;@ApiModelProperty(value = "返回数据")private Map<String, Object> data = new HashMap<String, Object>();//把构造方法私有 不让别人new该类private R() {}//实现链式编程//R.ok().code().message().data();//成功静态方法public static R ok() {R r = new R();r.setSuccess(true);r.setCode(ResultCode.SUCCESS);r.setMessage("成功");return r;}//失败静态方法public static R error() {R r = new R();r.setSuccess(false);r.setCode(ResultCode.ERROR);r.setMessage("失败");return r;}public R success(Boolean success){this.setSuccess(success);return this; //返回this 实现链式编程}//实现链式编程//R.ok().code().message().data();public R message(String message){this.setMessage(message);return this;}public R code(Integer code){this.setCode(code);return this;}public R data(String key, Object value){this.data.put(key, value);return this;}public R data(Map<String, Object> map){this.setData(map);return this;}}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
4. 项目结构

三. 使用
1.在使用的包中先导依赖
2.接口方法返回结果都改为R(导包时记得引对)
// 1 查询所有数据@GetMapping("findAll")public R findAllTeacher(){//调用service的方法实现查询所有的操作List<EduTeacher> list = teacherService.list(null);return R.ok().data("items",list); //链式编程}// 2. 逻辑删除方法@DeleteMapping("{id}")public R removeTeacher(@PathVariable String id){boolean flag = teacherService.removeById(id);if(flag){return R.ok();}elsereturn R.error();}
