前言
你是否在项目中或大部分开源项目中看见如下代码?
public Result<List<Entity>> getxxx(){
...
return Result.ok(data);
}
上述代码是一个非常简单的controller代码实例,使用通用的返回值包装数据。
但是这种写法写一次两次可能说还可以,但是在你的项目中出现大量的Result<泛型>这就使得你的项目变得愈发臃肿,controller中也存在这大量的重复代码。
那么有没有一种办法去解决这种统一返回值的处理呢?
答案是:有的!
ResponseBodyAdvice
ResponseBodyAdvice是SpringMVC提供的一个接口,方便开发者处理你的响应体,也就是我们后端接口返回的json数据/xml数据。
public interface ResponseBodyAdvice<T> {
/**
* Whether this component supports the given controller method return type
* and the selected {@code HttpMessageConverter} type.
* @param returnType the return type
* @param converterType the selected converter type
* @return {@code true} if {@link #beforeBodyWrite} should be invoked;
* {@code false} otherwise
*/
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
/**
* Invoked after an {@code HttpMessageConverter} is selected and just before
* its write method is invoked.
* @param body the body to be written
* @param returnType the return type of the controller method
* @param selectedContentType the content type selected through content negotiation
* @param selectedConverterType the converter type selected to write to the response
* @param request the current request
* @param response the current response
* @return the body that was passed in or a modified (possibly new) instance
*/
@Nullable
T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response);
}
ResponseBodyAdvice提供了两个方法需要我们去实现
supports (是否支持/允许进行返回值处理)
beforeBodyWrite (写入ResponseBody之前的方法,需返回具体返回的ResponseBody对象)
如何使用
在SpringBoot项目使用ResponseBodyAdvice需要确保你的pom中成功加载了Spring-web(SpringMVC)。
以gradle为实例,需要在你的build.gradle.kts中添加Spring-web。
dependencies {
...
implementation("org.springframework.boot:spring-boot-starter-web")
... //其他依赖
}
使用Java/Kotlin创建class ApiResultAdvice并实现ResponseBodyAdvice接口,并指定泛型为Object(因为Java所有类的父类均为Object,我们要对所有返回值进行处理,故此这里是Object)
@RestControllerAdvice
public class ApiResultAdvice implements ApiResultAdvice<Object> {
private final ObjectMapper objectMapper;
@Autowired
public ApiResultAdvice(ObjectMapper objectMapper) {
this.objectMapper = objectMapper
}
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
//根据项目需求
return true;
}
T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
if (Void.class.equals(returnType.parameterType)) {
return Result.ok(xxx); //void类型直接返回
}
if (body instanceof String) {
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
return objectMapper.writeValueAsString(Result.ok(xxx)); //处理字符串返回,自动包装成Json
}
return Result.ok(xxx);
}
}
@RestControllerAdvice
class ApiResultAdvice(
val objectMapper: ObjectMapper
) : ResponseBodyAdvice<Any> {
override fun supports(returnType: MethodParameter, converterType: Class<out HttpMessageConverter<*>>): Boolean {
//根据项目需求
return true;
}
override fun beforeBodyWrite(
body: Any?,
returnType: MethodParameter,
selectedContentType: MediaType,
selectedConverterType: Class<out HttpMessageConverter<*>>,
request: ServerHttpRequest,
response: ServerHttpResponse
): Any? {
// 处理无返回值(即 void)类型的方法
if (returnType.parameterType == Void::class.java) {
return Result.success(message)
}
// 处理 String 类型的返回值
if (body is String) {
// 用于重置字符串的响应类型为 application/json
response.headers.contentType = MediaType.APPLICATION_JSON
// 将统一对象转换成 json 字符串
return objectMapper.writeValueAsString(Result.success(message, body))
}
// 其他情况
return Result.success(message, body)
}
}
使用上述代码就可以将你的api接口包装成返回值统一的接口了,还减少了大量冗余代码。
是不是很神奇呢?赶快在你的项目中试一试吧!