如何优雅的处理SpringBoot接口的响应体
我们知道在开发,规范化的开发会节省很多时间,无论是代码规范还是接口规范,如果你们的开发模式是前后端分离的,那你要和前端开发工程师进行对接,不可能因为每个人的代码风格不统一来进行更改,统一的规范会让我们在开发过程事半功倍,下面就看看如何统一处理响应体。
【注意】在编写实体类的时候 为了简洁 ,我使用了 lombok 用 @Data 、@Setter 、@Getter 取消了get set 方法,如果你们觉得不习惯,可以自己手动添加。
以下对响应体的处理,不是绝对唯一的,可根据自己的业务需要,自己定义对应的响应方式。
一、定义一个响应实体类
定义了响应的实体类,code 代表响应状态码,msg代表响应信息,data 代表响应的具体数据 ,这里使用了泛型,为了可以更好的兼容不同类型的返回数据。
package com.dongl.utils.response; import lombok.Data; /** * @author D-L * @Version 1.0 * @Description 响应体实体类 * @Date 2020/8/26 */ @Data public class ResultVO<T> { /** * 状态码,比如200代表响应成功 */ private int code; /** * 响应信息,用来说明响应情况 */ private String msg; /** * 响应的具体数据 */ private T data; public ResultVO(T data) { this(ResultCode.SUCCESS, data); } public ResultVO(ResultCode resultCode, T data) { this.code = resultCode.getCode(); this.msg = resultCode.getMsg(); this.data = data; } }
二、定义一个响应状态码枚举类(统一响应状态)
这个响应状态码可以自己根据具体的业务自行定义:
package com.dongl.utils.response; import lombok.Getter; /** * @author D-L * @Version 1.0 * @Description 响应状态码枚举类 * @Date 2020/8/26 */ @Getter public enum ResultCode { SUCCESS(200, "操作成功"), FAILED(1001, "响应失败"), VALIDATE_FAILED(1002, "参数校验失败"), ERROR(5000, "未知错误"); private int code; private String msg; ResultCode(int code, String msg) { this.code = code; this.msg = msg; } }
还记得之前写过一篇 《如何优雅的处理SpringBoot接口中参数校验》其中有对异常的全局处理,这里可以对返回的响应数据进一步处理:
package com.dongl.utils.error; import com.dongl.utils.response.ResultCode; import com.dongl.utils.response.ResultVO; import org.springframework.validation.ObjectError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; /** * @author D-L * @Classname ExceptionControllerAdvice * @Version 1.0 * @Description 全局处理参数校验异常返回提示 * @Date 2020/8/26 */ @RestControllerAdvice public class ExceptionControllerAdvice { @ExceptionHandler(APIException.class) public ResultVO<String> APIExceptionHandler(APIException e) { // 注意哦,这里传递的响应码枚举 return new ResultVO<>(ResultCode.FAILED, e.getMsg()); } @ExceptionHandler(MethodArgumentNotValidException.class) public ResultVO<String> MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) { ObjectError objectError = e.getBindingResult().getAllErrors().get(0); // 注意哦,这里传递的响应码枚举 return new ResultVO<>(ResultCode.VALIDATE_FAILED, objectError.getDefaultMessage()); } }
返回响应体:
{ "code": 1002, "msg": "参数校验失败", "data": "需要在20和99之间" }
三、全局配置响应体(这里需要加上扫描的包)
这样定义完了响应体,就需要你在编写代码的同时,每一个接口都要添加对应的code msg data ,虽然勉强说的过去,但是总感觉差点意思,当然我们可以统一处理,先创建一个类加上注解使其成为全局处理类。然后继承ResponseBodyAdvice接口重写其中的方法,即可对我们的controller进行增强操作。
package com.dongl.utils.response; import com.dongl.utils.error.APIException; import com.fasterxml.jackson.core.JsonProcessingException; 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.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; /** * @author D-L * @date 2020-08-27 * @Description 全局配置 响应体 * Version 1.0 */ @RestControllerAdvice(basePackages = {"com.dongl.controller"}) // 注意哦,这里要加上需要扫描的包 public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> { @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> aClass) { // 如果接口返回的类型本身就是ResultVO那就没有必要进行额外的操作,返回false return !returnType.getGenericParameterType().equals(ResultVO.class); } @Override public 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("返回String类型错误"); } } // 将原本的数据包装在ResultVO里 return new ResultVO<>(data); } }
【总结】响应体的定义,以及状态码的定义,都可以自己根据自己的需要和业务的需要自行编写。