这可能是一个奇怪的问题,尽管我想知道为什么以前没有人提出过这个问题。。。因此,如果有任何无知,请纠正我。
首先,我将Jackson与Spring和@ResponseBody注释结合使用。目前,对于每个请求处理程序,我都返回一个“Response”包装器对象,这是客户机所期望的。这个包装非常简单:
{ "response": { "data" : ACTUAL_DATA } }
问题是,我不喜欢显式地包装所有请求处理程序的每个返回值。我也不喜欢在单元测试中打开这些响应包装。
相反,我想知道是否有可能返回实际的_数据,并在其他地方截取和包装这些数据。
如果这实际上是可能的,那么是否有可能读取附加到截获请求处理程序的注释?通过这种方式,我可以使用自定义注释来决定如何包装数据。
例如,像这样的东西将是惊人的(请注意,@FetchReport和@响应Wrapper是由建议的注释组成的):
@RequestMapping(...)
@FetchResponse
@ResponseBody
public List<User> getUsers() {
...
}
@ResponseWrapper(FetchResponse.class)
public Object wrap(Object value) {
ResponseWrapper rw = new ResponseWrapper();
rw.setData(value);
return rw;
}
有人熟悉这个地区吗?或者,为什么这可能是不好的做法?
我知道答案被接受已经有一段时间了,但最近我偶然发现了与Jackson的一个问题,这让我发现了使用ResponseBodyAdvice
的问题。
Jackson不会正确序列化使用@JsonTypeInfo
/@JsonSubTypes
的多态类型,如果在运行时类型的值未知:例如,如果您有一个类似类响应包装器的通用容器类型
如果您只是实现
ResponseBodyAdvice
并从beforeBodyWrite()
返回一个新的包装值,那么Spring将不再知道您的完整泛型类型及其专门化,它将把您的响应序列化为ResponseWrapper
解决此问题的唯一方法是从
AbstractJackson2HttpMessageConverter
扩展并重写writeInternal()
。请参见此处的方法如何处理类型:https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java#L437
您还需要使用AbstractMappingJacksonResponseBodyAdvice
和您自己的自定义MappingJacksonValue
实现控制器建议,其中包括自定义HttpMessageConverter将使用的类型targetType
。
响应包装器
public class ResponseWrapper<T> {
@Nullable Error error;
T result;
public ResponseWrapper(T result) {
this.result = result;
}
}
包装用具
@Component
public class WrappingAdvice extends AbstractMappingJacksonResponseBodyAdvice {
@Override
protected MappingJacksonValue getOrCreateContainer(Object body) {
MappingJacksonValue cnt = super.getOrCreateContainer(body);
if (cnt instanceof MyMappingJacksonValue) {
return cnt;
}
return new MyMappingJacksonValue(cnt);
}
@Override
protected void beforeBodyWriteInternal(
MappingJacksonValue bodyContainer, MediaType contentType,
MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
MyMappingJacksonValue cnt = (MyMappingJacksonValue) bodyContainer;
Type targetType = getTargetType(bodyContainer.getValue(), returnType);
cnt.setValue(new ResponseWrapper(cnt.getValue()));
cnt.setTargetType(TypeUtils.parameterize(
ResponseWrapper.class,
targetType));
}
/**
* This is derived from AbstractMessageConverterMethodProcessor
*/
private Type getTargetType(Object value, MethodParameter returnType) {
if (value instanceof CharSequence) {
return String.class;
}
Type genericType;
if (HttpEntity.class.isAssignableFrom(returnType.getParameterType())) {
genericType = ResolvableType.forType(returnType.getGenericParameterType()).getGeneric().getType();
} else {
genericType = returnType.getGenericParameterType();
}
return GenericTypeResolver.resolveType(genericType, returnType.getContainingClass());
}
public static class MyMappingJacksonValue extends MappingJacksonValue {
private Type targetType;
public MyMappingJacksonValue(MappingJacksonValue other) {
super(other.getValue());
setFilters(other.getFilters());
setSerializationView(other.getSerializationView());
}
public Type getTargetType() {
return targetType;
}
public void setTargetType(Type targetType) {
this.targetType = targetType;
}
}
}
JsonHttpMessageBodyConzer
@Component
public class JsonHttpMessageBodyConverter extends AbstractJackson2HttpMessageConverter {
// omitted all constructors
@Override
protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
if (object instanceof WrapAPIResponseAdvice.MyMappingJacksonValue) {
type = ((WrapAPIResponseAdvice.MyMappingJacksonValue) object).getTargetType();
}
super.writeInternal(object, type, outputMessage);
}
}
对于任何想了解更多关于这个话题的信息的人:我也面临着同样的问题,多亏了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);
}
}
此解决方案将控制器中返回的所有对象包装在一个(本例中)JSend响应类中,这为您节省了在所有控制器方法中返回JSend响应的麻烦。
嗯,看起来我在找Spring的“响应身体建议”和“抽象映射杰克逊响应身体建议”。
来自API的Im有两个不同的响应。然后我判断对象,但是我如何将消息的内容返回到字符串呢?下面的示例返回Exception: Json响应: Json响应: 模型:
我已经通过HTTP脚本记录器记录了一个脚本,但当我运行脚本时,我得到的对象移动到这里错误的登录事务的采样器上。当在记录的xml中搜索相同的请求时,我可以看到它给出了一个有效的输出。有人能告诉我为什么当我运行脚本时,只有它显示这个错误。请参考以下快照。 来自录制xml的请求:录制的\u请求\u正文 来自录制xml的请求头:录制的请求头 录制xml的请求-响应:录制的请求-响应 脚本请求:脚本请求正文
概述 Django 使用Request 对象和Response 对象在系统间传递状态。 当请求一个页面时,Django会建立一个包含请求元数据的 HttpRequest 对象。 当Django 加载对应的视图时,HttpRequest 对象将作为视图函数的第一个参数。每个视图会返回一个HttpResponse 对象。 本文档对HttpRequest 和HttpResponse 对象的API 进行说
Koa Response对象是节点的vanilla响应对象之上的抽象,提供了对日常HTTP服务器开发有用的附加功能。 Koa响应对象嵌入在上下文对象中, this 。 每当我们收到请求时,让我们注销响应对象。 var koa = require('koa'); var router = require('koa-router'); var app = koa(); var _ = router()
当响应被关闭时,容器必须立即刷出响应缓冲区中的所有剩余的内容到客户端。以下事件表明 servlet 满足了请求且响应对象即将关闭: servlet 的 service 方法终止。 响应的 setContentLength 或 setContentLengthLong 方法指定了大于零的内容量,且已经写入到响应。 sendError 方法已调用。5.6 sendRedirect 方法已调用。 Asy
res对象表示Express应用程序在收到HTTP请求时发送的HTTP响应。 响应对象属性 以下是与响应对象关联的一些属性的列表。 Sr.No. 属性和描述 1 res.app 此属性包含对使用中间件的快速应用程序实例的引用。 2 res.headersSent 布尔属性,指示应用程序是否为响应发送了HTTP标头。 3 res.locals 包含作用于请求的响应局部变量的对象 响应对象方法 res