在上一篇中,当在Controller类中需要返回统一格式的数据时,需要实例化一个R,有时候觉得还是不够简洁,那有没有一种方法Controller中直接返回对象,但是返回的对象统一保存到如下格式的data中?
ResponseBodyAdvice
ResponseBodyAdvice 是 Spring MVC 框架中的一个接口,它允许你在响应体被写入之前对其进行处理。这对于实现自定义的响应格式、压缩响应、加密响应等场景非常有用。
ResponseBodyAdvice 接口有两个主要方法:
supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType): 这个方法用于判断当前 ResponseBodyAdvice 是否适用于特定的方法返回类型和 HTTP 消息转换器类型。如果返回 true,则 beforeBodyWrite 方法将被调用;如果返回 false,则不会被调用。
beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response): 这个方法在响应体被写入之前被调用。你可以在这个方法中对响应体进行修改,例如修改响应头、修改响应体内容等。这个方法的返回值将作为最终的响应体被写入。
要使用 ResponseBodyAdvice,你需要创建一个类并实现这个接口,然后将其注册为一个 Spring Bean。下面是一个简单的示例:
@ControllerAdvice
public class CustomResponseBodyAdvice implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
// 根据需要判断是否应用到指定的方法返回类型和转换器类型
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
// 在这里对响应体进行处理,例如包装成自定义的响应格式
CustomResponseWrapper customResponse = new CustomResponseWrapper(body);
return customResponse;
}
}
以下为示例代码:
package org.example.web.web;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.web.model.R;
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.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.util.Map;
/**
* 对controller 层中 ResponseBody 注解方法,进行增强拦截
*/
@ControllerAdvice
public class ResultResponseAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
// 返回true表示对所有Controller的返回值进行处理
return true;
}
/**
* 如果开启,就会对返回结果进行处理
*/
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
// 设置响应类型为json
response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);
if (body instanceof R) {
// 如果body返回的是ResultMsg类型的对象,不进行增强处理
return body;
}
if (body instanceof String) {
// 如果body返回的是String类型的对象,单独处理
return toJson(body);
}
if (body instanceof Map) {
// 对异常进行统一处理
Map<String, Object> map = (Map<String, Object>) body;
if (map.containsKey("code") && map.containsKey("message")) {
if ((int)map.get("code") == 200) {
return R.ok(map.get("message").toString());
} else {
return R.error((int)map.get("code"), map.get("message").toString());
}
} else {
return R.ok(map);
}
}
return R.ok(body);
}
private Object toJson(Object body) {
try {
return new ObjectMapper().writeValueAsString(R.ok(body));
} catch (JsonProcessingException e) {
throw new RuntimeException("无法转发json格式", e);
}
}
}
示例
这样当在Controller中类似这样的处理时,返回的结果就更加简洁了。
@RequestMapping("/user")
@ResponseBody
public R<User> user() {
User user = User.builder().id("1").username("test").name("张三").age(20).createTime(new Date()).build();
return R.ok(user);
}
@RequestMapping("/user2")
@ResponseBody
public User user2() {
return User.builder().id("1").username("test").name("张三").age(20).createTime(new Date()).build();
}
浏览器访问截图如下:
可见这种写法更简洁统一。