在上一篇中,当在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> {@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {// 返回true表示对所有Controller的返回值进行处理return true;}/*** 如果开启,就会对返回结果进行处理*/@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request, ServerHttpResponse response) {// 设置响应类型为jsonresponse.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")@ResponseBodypublic R<User> user() {User user = User.builder().id("1").username("test").name("张三").age(20).createTime(new Date()).build();return R.ok(user);}@RequestMapping("/user2")@ResponseBodypublic User user2() {return User.builder().id("1").username("test").name("张三").age(20).createTime(new Date()).build();}
浏览器访问截图如下:
可见这种写法更简洁统一。