在项目开发过程中,很难避免返回各种各样的结果,就会导致前端渲染时造成混乱,怎么统一返回呢?
编写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.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);
}
}
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
*/
@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;
}
}
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
*/
@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 "成功";
}
}
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: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);
*/
}
}
分界
为什么使用
项目中我们会将响应封装成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. 创建结果类
//统一返回结果的类
@Data
public 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();
}else
return R.error();
}