这可能是一个奇怪的问题,虽然我想知道为什么它之前没有被问过或提出过......所以如果有任何无知,请纠正我。
首先,我使用Jackson和Spring以及@ResponseBody注释。 目前,对于每个请求处理程序,我都会返回一个"响应"包装器对象,因为这是客户期望的。这个包装很简单:
{ "response": { "data" : ACTUAL_DATA } }
事情是,我不是明确包装所有请求处理程序的每个返回值的粉丝。我也不喜欢在我的单元测试中打开这些响应包装器。
相反,我想知道是否可以按原样返回ACTUAL_DATA,并拦截并将这些数据包装到别处。
如果这实际上是可行的,那么是否可以读取附加到截获的请求处理程序的注释?这样我就可以使用自定义注释来决定如何包装数据。
例如,这样的事情会很棒(注意@FetchResponse和@ResponseWrapper是由建议的注释构成的):
@RequestMapping(...)
@FetchResponse
@ResponseBody
public List<User> getUsers() {
...
}
@ResponseWrapper(FetchResponse.class)
public Object wrap(Object value) {
ResponseWrapper rw = new ResponseWrapper();
rw.setData(value);
return rw;
}
任何熟悉此领域的人?或者,为什么这可能是不好的做法?
答案 0 :(得分:1)
好吧,看起来我正在寻找Spring的“ResponseBodyAdvice”和“AbstractMappingJacksonResponseBodyAdvice”。
答案 1 :(得分:1)
对于有关此主题的更多信息的任何人:我也面临同样的问题,并且由于kennyg的提示,我设法提出以下解决方案:
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice
public class JSendAdvice implements ResponseBodyAdvice<Object> {
@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) {
if (body instanceof JSendResponse) {
return body;
}
return new JSendResponse<>().success(body);
}
}
此解决方案将控制器中返回的所有对象包装在(对于此示例)JSendResponse类中,这样可以省去在所有控制器方法中返回JSendResponses的麻烦。
答案 2 :(得分:0)
我知道答案被接受已经有一段时间了,但我最近偶然发现了 Jackson 的一个问题,这让我发现了使用 ResponseBodyAdvice
的问题。
如果在运行时类型的值未知,Jackson 将不会正确序列化使用 @JsonTypeInfo
/ @JsonSubTypes
的多态类型:例如,如果您有一个像 {{1} 这样的通用容器类型}}。也就是说,除非您在要求 Jackson 序列化您的值之前向 Jackson 提供该泛型类型的特化,请参阅 Why does Jackson polymorphic serialization not work in lists? 。当您返回一个 T 列表并且 class ResponseWrapper<T> { List<T> objects; }
是已知的时,Spring 会为您执行此操作,因为它在方法返回类型中明确提供(如 T
)。
如果您只是实现 public List<MyEntity> getAllEntities();
并从 ResponseBodyAdvice
返回一个新的包装值,那么 Spring 将不再知道您的完整泛型类型及其特化,它会将您的响应序列化为 {{1} } 而不是 beforeBodyWrite()
。
解决此问题的唯一方法是从 ResponseWrapper<?>
扩展并覆盖 ResponseWrapper<MyEntity>
。在此处查看该方法如何处理类型:https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java#L437
并且您还需要使用 AbstractJackson2HttpMessageConverter
和您自己的自定义 writeInternal()
实现控制器通知,其中包括自定义 HttpMessageConverter 将使用的 AbstractMappingJacksonResponseBodyAdvice
。
响应包装器
MappingJacksonValue
包装建议
Type targetType
JsonHttpMessageBodyConverter
public class ResponseWrapper<T> {
@Nullable Error error;
T result;
public ResponseWrapper(T result) {
this.result = result;
}
}