Java Spring+Jackson:在响应对象中包装响应体

Java Spring+Jackson:在响应对象中包装响应体,java,spring,jackson,Java,Spring,Jackson,这可能是一个奇怪的问题,尽管我想知道为什么以前没有人提出过这个问题。。。因此,如果有任何无知,请纠正我 首先,我将Jackson与Spring和@ResponseBody注释结合使用。 目前,对于每个请求处理程序,我都返回一个响应包装器对象,这是客户机所期望的。这个包装非常简单: { "response": { "data" : ACTUAL_DATA } } 问题是,我不喜欢显式地包装所有请求处理程序的每个返回值。我也不喜欢在单元测试中打开这些响应包装 相反,我想知道是否有可能返回实际的_数

这可能是一个奇怪的问题,尽管我想知道为什么以前没有人提出过这个问题。。。因此,如果有任何无知,请纠正我

首先,我将Jackson与Spring和@ResponseBody注释结合使用。 目前,对于每个请求处理程序,我都返回一个响应包装器对象,这是客户机所期望的。这个包装非常简单:

{ "response": { "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;
}

有人熟悉这个地区吗?或者,这可能是不好的做法的原因是什么?

好吧,看起来我正在寻找Spring的ResponseBodyAdvice和AbstractMappingJacksonResponseBodyAdvice。

任何人如果想了解更多关于这个话题的信息,我也面临着同样的问题,多亏了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的麻烦。

我知道答案被接受已经有一段时间了,但我最近偶然发现了Jackson的一个问题,这让我发现了使用ResponseByAdvice的问题

如果在运行时类型的值未知,Jackson将无法正确序列化使用@JsonTypeInfo/@JsonSubTypes的多态类型:例如,如果您具有类ResponseWrapper{List objects;}之类的通用容器类型。除非您在请求Jackson序列化您的值之前为其提供该泛型类型的专门化,否则请参阅。当您返回一个T的列表,并且T是已知的,因为它是在方法return type中显式提供的,如public list getAllenties;中一样,Spring会为您这样做

如果您只是实现ResponseByAdvice并从beforeBodyWrite返回一个新的包装值,那么Spring将不再知道您的完整泛型类型及其专门化,它将把您的响应序列化为ResponseWrapper而不是ResponseWrapper

唯一的解决方法是从AbstractJackson2HttpMessageConverter扩展并重写writeInternal。请参见此处的方法如何处理类型: 您还需要使用AbstractMappingJacksonResponseBodyAdvice和您自己的自定义MappingJacksonValue实现控制器建议,该值包括自定义HttpMessageConverter将使用的类型targetType

响应包装器

公共类响应包装器{ @可为空的错误; T结果; 公共响应包装结果{ this.result=结果; } } 包装用具

@组成部分 公共类包装扩展了AbstractMappingJacksonResponseBodyAdvice{ @凌驾 受保护映射JacksonValue getOrCreateContainerObject主体{ MappingJacksonValue cnt=super.getOrCreateContainerbody; 如果MyMappingJacksonValue的cnt实例{ 返回cnt; } 返回新的MyMappingJacksonValuecnt; } @凌驾 BodyWriteInternal之前的受保护无效 MappingJacksonValue bodyContainer,MediaType contentType, MethodParameter返回类型、ServerHttpRequest请求、ServerHttpResponse响应{ MyMappingJacksonValue cnt=MyMappingJacksonValue bodyContainer; 类型targetType=getTargetTypebodyContainer.getValue,returnType; cnt.setValuenew ResponseWrappercnt.getValue; cnt.setTargetTypeUtils.parameterize ResponseWrapper.class, 靶型; } /** *这是从AbstractMessageConverterMethodProcessor派生的 */ 私有类型getTargetTypeObject值,方法参数returnType{ CharSequence的if值实例{ 返回字符串.class; } 类型genericType; 如果HttpEntity.class.isAssignableFromreturnType.getParameterType{ genericType=ResolvableType.forTypereturnType.getGenericParameterType.getGeneric.getType; }否则{ genericType=returnType.getGenericParameterType; } return GenericTypeResolver.resolveTypegenericType,returnType.getContainingClass; } 公共静态类MyMappingJacksonValue扩展了MappingJacksonValue{ 私有类型targetType; 公共MyMappingJacksonValue MappingJacksonValue其他{ superother.getValue; setFiltersother.getFilters; setSerializationViewother.getSerializationView; } 公共类型getTargetType{ 返回目标类型; } public void setTargetTypeType targetType{ this.targetType=targetType; } } } JsonHttpMessageBodyConverter

@组成部分 公共类JsonHttpMessageBodyConverter扩展了AbstractJackson2HttpMessageConverter{ //省略所有构造函数 @凌驾 受保护的void writeInternalObject对象,类型类型,HttpOutputMessage outputMessage抛出IOException,HttpMessageGenetWritableException{ 如果WrapAPIResponseAdvice.MyMappingJacksonValue的对象实例{ type=WrapAPIResponseAdvice.MyMappingJacksonValue object.getTargetType; } super.writeInternalobject,类型,outputMessage; } }
简单的旧模型和视图,其中模型是您的响应,对您不起作用吗?实际上,我对模型和视图并不熟悉。我会查的,谢谢。是的,AOP可能是你想要的。或者,您可以提供自己的消息转换器。