文章目录
1. 背景说明
这里类似于统一异常处理,不熟悉的话,可以学习下。在做前后端分离的项目中,我们想要统一每个服务返回给前端的数据格式,比如所有的接口都以下面这种数据格式响应给前端:
{
"code": 0,
"message": "succeed",
"data": {
}
}
2. 封装响应数据类 ApiResponse
@Data
public class ApiResponse<T> {
public static final int CODE_OK = 0;
public static final int CODE_ERR_COMMON = -1;
public static final int CODE_PARAMETER_VALIDATE_FAILED = 1;
public static final String MSG_SUCCEED = "succeed";
private static final ApiResponse<Object> OK = new ApiResponse<>(CODE_OK, MSG_SUCCEED);
private int code;
private String message;
@JsonInclude(JsonInclude.Include.NON_NULL)
private T data;
public ApiResponse() { }
public ApiResponse(int code, String message) {
this.code = code;
this.message = message;
}
public ApiResponse(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static ApiResponse<Object> ok() {
return OK;
}
public static ApiResponse<Object> newInstance(int code, String message) {
return new ApiResponse<>(code, message);
}
public static <E> ApiResponse<E> newInstance(int code, String message, E data) {
return new ApiResponse<>(code, message, data);
}
public static <E> ApiResponse<E> newInstance(E data) {
try {
return new ApiResponse<>(CODE_OK,"success", data);
} catch (Exception e) {
return new ApiResponse<>(CODE_OK, MSG_SUCCEED, data);
}
}
}
3. 自定义注解 @ResponseResult
/**
* 此注解用于返回值包装,将返回值包装成{@link com.hh.vo.ApiResponse}对象
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseResult {
}
4. 自定义 ResponseAdvice 实现 ResponseBodyAdvice 接口
实现ResponseBodyAdvice接口,其实是对加了@RestController(也就是@Controller+@ResponseBody)注解的处理器将要返回的值进行增强处理。此接口对添加了@Controller和@ResponseBody的类织入一个通知 (增强功能)
/**
* 返回值统一处理类
*/
@Component
@Slf4j
// Controller层加了@ResponseResult注解的所有响应对象都会经过此拦截器,并对响应体进行封装
@RestControllerAdvice(annotations = ResponseResult.class)
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
/**
* 通过该方法可以选择哪些类或那些方法的response要进行处理, 其他的不进行处理.
*
* @param returnType 方法返回的类型
* @param converterType 参数类型装换
* @return true,则下面 beforeBodyWrite方法被调用, 否则就不调用下述方法
*/
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
// 方法的返回类型
Class<?> type = returnType.getParameterType();
log.debug("return type is :{}", type);
return !ApiResponse.class.isAssignableFrom(type)
&& !ModelAndView.class.isAssignableFrom(type)
&& !CharSequence.class.isAssignableFrom(type)
&& !ResponseEntity.class.isAssignableFrom(type);
}
/**
* 对response方法进行具体操作处理
*
* @param body 响应体
* @param returnType 返回值类型
* @param selectedContentType
* @param selectedConverterType
* @param request 当前请求
* @param response 当前响应
* @return
*/
@Override
public Object beforeBodyWrite(
Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response
) {
log.debug("going to convert response body to ApiResponse");
// 封装返回给前端的响应体
return ApiResponse.newInstance(body);
}
}
ResponseBodyAdvice 接口源码:
/**
* Allows customizing the response after the execution of an {@code @ResponseBody}
* or a {@code ResponseEntity} controller method but before the body is written
* with an {@code HttpMessageConverter}.
*/
public interface ResponseBodyAdvice<T> {
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
@Nullable
T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response);
}
5. 使用自定义注解 @ResponseResult
@Data
public class UserEntity {
private Integer id;
private String name;
private String password;
private Date createTime;
}
@ResponseResult
@RestController
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/add")
public UserEntity add(@RequestBody UserEntity userEntity){
// 可以看到并没有对响体 UserEntity 按照前后端交互的数据响应格式进行封装
return userService.insertUser(userEntity);
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
@CheckParam
public UserEntity insertUser(UserEntity userEntity){
int insert = userDao.insert(userEntity);
return userEntity;
}
}
可以看到,只要给Controller层控制器加上@ResponseResult注解,就可以对响应数据格式进行统一处理: