当前位置: 代码迷 >> 综合 >> Springboot前后端分离项目统一封装返回结果
  详细解决方案

Springboot前后端分离项目统一封装返回结果

热度:85   发布时间:2023-09-29 19:37:55.0

目录

统一返回数据格式

定义返回码枚举

定义返回类

问题

新增不进行封装注解


统一返回数据格式

项目中我们会将响应封装成json返回,一般我们会将所有接口的数据格式统一, 使前端(iOS Android, Web)对数据的操作更一致、轻松。 一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数 据就可以。但是一般会包含状态码、返回消息、数据这几部分内容

{"success": 布尔, //响应是否成功"code": 数字, //响应码"message": 字符串, //返回消息"data": HashMap //返回数据,放在键值对中
}

定义返回码枚举

/*** 统一返回状态码*/
public enum ResultEnum{/* 成功 */SUCCESS(200, "成功"),/*网络异常、错误*/ERROR(500,"网络异常"),/* 参数错误:1000~1999 */PARAM_NOT_VALID(1001, "参数无效"),PARAM_IS_BLANK(1002, "参数为空"),PARAM_TYPE_ERROR(1003, "参数类型错误"),PARAM_NOT_COMPLETE(1004, "参数缺失");private int code;private String msg;ResultEnum(int code, String msg){this.code = code;this.msg = msg;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}/*** 根据code获取message** @param code 状态码* @return msg*/public static String getMsgByCode(Integer code) {for (ResultEnum ele : values()) {if (ele.getCode()==code) {return ele.getMsg();}}return null;}
}

定义返回类

/*** 封装统一返回实体类* 继承HashMap 可随时put自定义key-value*/
public class Result extends HashMap<String,Object> {/** 状态码 */public static final String CODE_TAG = "code";/** 消息 */public static final String MSG_TAG = "msg";/** 数据对象 */public static final String DATA_TAG = "data";public Result() {}public Result(int code, String msg) {super.put(CODE_TAG, code);super.put(MSG_TAG, msg);}public Result(Integer code, String msg, Object obj) {super.put(CODE_TAG, code);super.put(MSG_TAG, msg);if (obj!=null){super.put(DATA_TAG, obj);}}public static Result success(){return new Result(ResultEnum.SUCCESS.getCode(),ResultEnum.SUCCESS.getMsg());}public static Result success(Object obj){return new Result(ResultEnum.SUCCESS.getCode(),ResultEnum.SUCCESS.getMsg(),obj);}public static Result error(){return new Result(ResultEnum.ERROR.getCode(),ResultEnum.ERROR.getMsg());}public static Result error(String msg){return new Result(ResultEnum.ERROR.getCode(),msg);}public static Result error(Integer code,String msg){return new Result(code,msg);}}

问题

前后端分离的项目中,基本每个controller都要返回一个resultVo,如下

return new ResultVo(data);

如果就想返回一个实体!可以通过AOP拦截所有Controller,再@After的时候统一封装

@RestControllerAdvice(basePackages = {"com.system"})
public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {// response是ResultVo类型,或者注释了NotControllerResponseAdvice都不进行包装return !methodParameter.getParameterType().isAssignableFrom(ResultVo.class);}@Overridepublic Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest request, ServerHttpResponse response) {// String类型不能直接包装if (returnType.getGenericParameterType().equals(String.class)) {ObjectMapper objectMapper = new ObjectMapper();try {// 将数据包装在ResultVo里后转换为json串进行返回return objectMapper.writeValueAsString(new ResultVo(data));} catch (JsonProcessingException e) {throw new APIException(ResultCode.RESPONSE_PACK_ERROR, e.getMessage());}}// 否则直接包装成ResultVo返回return new ResultVo(data);}
}
  1. @RestControllerAdvice(basePackages = {"com.bugpool.leilema"})自动扫描了所有指定包下的controller,在Response时进行统一处理
  2. 重写supports方法,也就是说,当返回类型已经是ResultVo了,那就不需要封装了,当不等与ResultVo时才进行调用beforeBodyWrite方法,跟过滤器的效果是一样的
  3. 最后重写我们的封装方法beforeBodyWrite,注意除了String的返回值有点特殊,无法直接封装成json,我们需要进行特殊处理,其他的直接new ResultVo(data);就ok了

测试

    @PostMapping("/findByVo")public ProductInfo findByVo(@Validated ProductInfoVo vo) {ProductInfo productInfo = new ProductInfo();BeanUtils.copyProperties(vo, productInfo);return productInfoService.getOne(new QueryWrapper(productInfo));}

此时就算我们返回的是po,接收到的返回就是标准格式了

{"code": 1000,"msg": "请求成功","data": {"productId": 1,"productName": "泡脚","productPrice": 100.00,"productDescription": "中药泡脚加按摩","productStatus": 0,...}
}

新增不进行封装注解

因为百分之99的请求还是需要包装的,只有个别不需要,写在包装的过滤器吧?又不是很好维护,那就加个注解好了。所有不需要包装的就加上这个注解。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotControllerResponseAdvice {
}

然后在我们的增强过滤方法上过滤包含这个注解的方法

@RestControllerAdvice(basePackages = {"com.system"})
public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {//response是Result类型,或者注释了NotControllerResponseAdvice都不进行包装return !(methodParameter.getParameterType().isAssignableFrom(Result.class)|| methodParameter.hasMethodAnnotation(NotControllerResponseAdvice.class));}@Overridepublic Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest request, ServerHttpResponse response) {// String类型不能直接包装if (returnType.getGenericParameterType().equals(String.class)) {ObjectMapper objectMapper = new ObjectMapper();try {// 将数据包装在ResultVo里后转换为json串进行返回return objectMapper.writeValueAsString(Result.ok(data));} catch (JsonProcessingException e) {throw new CustomException(e.getMessage());}}// 否则直接包装成ResultVo返回return Result.ok(data);}
}

最后就在不需要包装的方法上加上注解

@RestController
public class HealthController {@GetMapping("/test")@NotControllerResponseAdvicepublic String health() {return "test";}
}