Why
在前后端分离的前提下,大部分的数据传输格式为JSON,因此,定义一个统一的响应数据格式更加有利于前后端的交互。
How
需要返回什么
是否响应成功 用于前端判断是否成功
响应状态码
状态码描述
业务响应数据
响应码接口
考虑后期业务优化和拓展,定义返回响应码接口,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public interface IResultCode extends Serializable { String getMessage () ; int getCode () ; }
返回响应码实现
为了后期方便维护,定义枚举维护响应码,此处的响应码使用Java Http中定义的响应码,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 @Getter @AllArgsConstructor public enum ResultCode implements IResultCode { SUCCESS(HttpServletResponse.SC_OK, "操作成功" ), FAILURE(HttpServletResponse.SC_BAD_REQUEST, "业务异常" ), UN_AUTHORIZED(HttpServletResponse.SC_UNAUTHORIZED, "请求未授权" ), INTERNAL_SERVER_ERROR(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "服务器异常" ), PARAM_MISS(HttpServletResponse.SC_BAD_REQUEST, "缺少必要的请求参数" ), PARAM_TYPE_ERROR(HttpServletResponse.SC_BAD_REQUEST, "请求参数类型错误" ), PARAM_BIND_ERROR(HttpServletResponse.SC_BAD_REQUEST, "请求参数绑定错误" ), PARAM_VALID_ERROR(HttpServletResponse.SC_BAD_REQUEST, "参数校验失败" ), ; final int code; final String message; }
定义统一结果类
注意
外部只能调用统一返回类的方法,不可以直接创建
内置静态方法返回对象。
使用链式编程,方便开发(方法都返回对象本身return this;
)。
业务响应信息类型由实际使用者定义(使用泛型接收)。
代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 @Getter public class R <T > implements Serializable { private static final long serialVersionUID = 1L ; private int code; private boolean success; private T data; private String message; private R (IResultCode resultCode,T data,String message) { this .code = resultCode.getCode(); this .data = data; this .message = message==null ?resultCode.getMessage():message; this .success = this .code == ResultCode.SUCCESS.code; } private R (IResultCode resultCode,T data) { this (resultCode,data,null ); } private R (IResultCode resultCode) { this (resultCode,null ,null ); } private R (IResultCode resultCode,String message) { this (resultCode,null ,message); } public static <T> R<T> success () { return new R<T>(ResultCode.SUCCESS); } public static <T> R<T> success (T data) { return new R<>(ResultCode.SUCCESS,data); } public static <T> R<T> success (T data,String message) { return new R<>(ResultCode.SUCCESS,data,message); } public static <T> R<T> fail () { return new R<>(ResultCode.FAILURE); } public static <T> R<T> fail (String message) { return new R<>(ResultCode.FAILURE,message); } public static <T> R<T> fail (IResultCode resultCode) { return new R<>(resultCode); } public static <T> R<T> fail (IResultCode resultCode,String message) { return new R<>(resultCode,message); } public static <T> R<T> instance (ResultCode resultCode,T data,String message) { return new R<>(resultCode,data,message); } public R message (String message) { this .message = message; return this ; } public R success (boolean success) { this .success = success; return this ; } public R data (T data) { this .data = data; return this ; } }
测试R类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class RTest { public static void main (String[] args) { System.out.println("\n——————————————————成功————————————————————————" ); System.out.println("无承载数据成功__" + JSON.toJSONString(R.success())); System.out.println("承载数据成功__" +JSON.toJSONString(R.success(new String[]{"承载数据" }))); System.out.println("承载数据并修改描述成功__" +JSON.toJSONString(R.success(new String[]{"承载数据" },"修改的描述信息" ))); System.out.println("\n——————————————————失败————————————————————————" ); System.out.println("默认失败__" +JSON.toJSONString(R.fail())); System.out.println("修改错误信息失败__" +JSON.toJSONString(R.fail("修改后的错误信息" ))); System.out.println("修改ResultCode(缺失参数)失败__" +JSON.toJSONString(R.fail(ResultCode.PARAM_MISS))); System.out.println("修改ResultCode并自定义信息失败__" +JSON.toJSONString(R.fail(ResultCode.PARAM_MISS,"请检查你的请求参数" ))); System.out.println("\n——————————————————特殊————————————————————————" ); System.out.println("自定义__" +JSON.toJSONString(R.instance(ResultCode.SUCCESS,new String[]{"承载数据" },"自定义的返回信息" ))); System.out.println("\n——————————————————创建后修改————————————————————————" ); System.out.println("修改消息__" +R.success().message("创建之后修改的消息" )); System.out.println("修改数据__" +R.success("11" ).data("创建之后修改的承载数据" )); System.out.println("修改成功标识__" +R.success().success(false )); } }
优化
现存在的问题
每个Controller中的method在处理结束后都需要返回R.success();与实际业务并无关系,属于冗余代码。
问题处理
利用ControllerAdvice重写response,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @ControllerAdvice public class RespReultHandler implements ResponseBodyAdvice <Object > { private static final Class<? extends Annotation> IGNORE_ANNOTATION_TYPE = IgnoreRespResult.class; @Override public boolean supports (MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) { return !AnnotatedElementUtils.hasAnnotation(methodParameter.getContainingClass(),IGNORE_ANNOTATION_TYPE) && !methodParameter.hasMethodAnnotation(IGNORE_ANNOTATION_TYPE); } @Override public Object beforeBodyWrite (Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { if (o instanceof R) { return o; } return R.success(o); } }
IgnoreRespResult代码如下
1 2 3 4 @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE,ElementType.METHOD}) public @interface IgnoreRespResult { }
注意:这里可能会有其他问题
当返回值是String时,会出现java.lang.ClassCastException: com.**.R cannot be cast to java.lang.String,这是由于默认消息处理类不支持,解决方式如下,加入以下配置后可解决此问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Configuration @AllArgsConstructor @Order(Ordered.HIGHEST_PRECEDENCE) public class MessageConfiguration implements WebMvcConfigurer { private final ObjectMapper objectMapper; @Override public void configureMessageConverters (List<HttpMessageConverter<?>> converters) { FastJsonHttpMessageConverter messageConverter = new FastJsonHttpMessageConverter(); FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures( SerializerFeature.QuoteFieldNames, SerializerFeature.WriteMapNullValue, SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteDateUseDateFormat, SerializerFeature.WriteNullStringAsEmpty); List<MediaType> mediaTypeList = new ArrayList<>(); mediaTypeList.add(MediaType.APPLICATION_JSON); messageConverter.setSupportedMediaTypes(mediaTypeList); messageConverter.setFastJsonConfig(fastJsonConfig); converters.add(0 , messageConverter); } }
备注
本篇使用到的maven插件有
lombok
简化代码
javax.servlet-api
HTTP 状态码
fastjson
测试是输出json格式
参考自MybatisPlus中R的设计
源码链接GitHub