【发布时间】:2016-04-22 16:16:48
【问题描述】:
我有以下ContextResolver<ObjectMapper> 实现,它基于查询参数应该返回相应的 JSON 映射器(pretty/DateToUtc/Both):
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JsonMapper implements ContextResolver<ObjectMapper> {
private ObjectMapper prettyPrintObjectMapper;
private ObjectMapper dateToUtcMapper;
private ObjectMapper bothMapper;
private UriInfo uriInfoContext;
public JsonMapper(@Context UriInfo uriInfoContext) throws Exception {
this.uriInfoContext = uriInfoContext;
this.prettyPrintObjectMapper = new ObjectMapper();
this.prettyPrintObjectMapper.enable(SerializationFeature.INDENT_OUTPUT);
this.dateToUtcMapper = new ObjectMapper();
this.dateToUtcMapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
this.bothMapper = new ObjectMapper();
this.bothMapper.enable(SerializationFeature.INDENT_OUTPUT);
this.bothMapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
@Override
public ObjectMapper getContext(Class<?> objectType) {
System.out.println("hi");
try {
MultivaluedMap<String, String> queryParameters = uriInfoContext.getQueryParameters();
Boolean containsPretty = queryParameters.containsKey("pretty");
Boolean containsDate = queryParameters.containsKey("date_to_utc");
Boolean containsBoth = containsPretty && containsDate;
if (containsBoth) {
System.out.println("Returning containsBoth");
return bothMapper;
}
if (containsDate) {
System.out.println("Returning containsDate");
return dateToUtcMapper;
}
if (containsPretty) {
System.out.println("Returning pretty");
return prettyPrintObjectMapper;
}
} catch(Exception e) {
// protect from invalid access to uriInfoContext.getQueryParameters()
}
System.out.println("Returning null");
return null; // use default mapper
}
}
还有以下主要应用:
private Server configureServer() {
ObjectMapper mapper = new ObjectMapper();
ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.packages(Calculator.class.getPackage().getName());
resourceConfig.property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
// @ValidateOnExecution annotations on subclasses won't cause errors.
resourceConfig.property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true);
resourceConfig.register(JacksonFeature.class);
resourceConfig.register(JsonMapper.class);
resourceConfig.register(AuthFilter.class);
ServletContainer servletContainer = new ServletContainer(resourceConfig);
ServletHolder sh = new ServletHolder(servletContainer);
Server server = new Server(serverPort);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
context.addServlet(sh, "/*");
server.setHandler(context);
return server;
}
但是,getContext() 函数在整个服务器生命周期内仅调用一次,仅在第一次请求时调用。这个类的整个想法是在运行时根据 url 参数确定映射器是什么。
更新
getContext() 为每个 uri 路径调用一次。例如,http://server/path1?pretty=true 将为对 /path1 的所有请求产生漂亮的输出,无论它们未来如何漂亮的queryParam。对 path2 的调用将再次调用 getContext,但不会对未来的 path2 调用。
更新2
好吧,似乎每个类都调用了一次GetContext,并为该特定类缓存了它。这就是为什么它需要一个类作为参数。所以看起来@LouisF 是对的,objectMapper 不适合条件序列化。但是,ContainerResponseFilter 替代方案部分有效,但未公开 ObjectMapper 功能,例如将日期转换为 UTC。所以我现在很困惑什么是条件序列化最合适的解决方案。
已解决
在@LoisF 的帮助下,我设法使用ContainerResponseFilter 进行条件序列化。我没有使用ContextResolver。以下是工作示例:
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.Provider;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.jaxrs.cfg.EndpointConfigBase;
import com.fasterxml.jackson.jaxrs.cfg.ObjectWriterInjector;
import com.fasterxml.jackson.jaxrs.cfg.ObjectWriterModifier;
/**
* Created by matt on 17/01/2016.
*/
@Provider
public class ResultTransformer implements ContainerResponseFilter {
public static final String OUTPUT_FORMAT_HEADER = "X-Output-Format";
public static final ObjectMapper MAPPER = new ObjectMapper();
public static class OutputFormat {
Boolean pretty = true;
Boolean dateAsTimestamp = false;
public Boolean getPretty() {
return pretty;
}
public void setPretty(Boolean pretty) {
this.pretty = pretty;
}
@JsonProperty("date_as_timestamp")
public Boolean getDateAsTimestamp() {
return dateAsTimestamp;
}
public void setDateAsTimestamp(Boolean dateAsTimestamp) {
this.dateAsTimestamp = dateAsTimestamp;
}
}
@Override
public void filter(ContainerRequestContext reqCtx, ContainerResponseContext respCtx) throws IOException {
String outputFormatStr = reqCtx.getHeaderString(OUTPUT_FORMAT_HEADER);
OutputFormat outputFormat;
if (outputFormatStr == null) {
outputFormat = new OutputFormat();
} else {
try {
outputFormat = MAPPER.readValue(outputFormatStr, OutputFormat.class);
ObjectWriterInjector.set(new IndentingModifier(outputFormat));
} catch (Exception e) {
e.printStackTrace();
ObjectWriterInjector.set(new IndentingModifier(new OutputFormat()));
}
}
}
public static class IndentingModifier extends ObjectWriterModifier {
private OutputFormat outputFormat;
public IndentingModifier(OutputFormat outputFormat) {
this.outputFormat = outputFormat;
}
@Override
public ObjectWriter modify(EndpointConfigBase<?> endpointConfigBase, MultivaluedMap<String, Object> multivaluedMap, Object o, ObjectWriter objectWriter, JsonGenerator jsonGenerator) throws IOException {
if(outputFormat.getPretty()) jsonGenerator.useDefaultPrettyPrinter();
if (outputFormat.dateAsTimestamp) {
objectWriter = objectWriter.with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
} else {
objectWriter = objectWriter.without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
return objectWriter;
}
}
}
【问题讨论】:
-
所以你想为每个请求确定使用哪个映射器?
标签: java jersey jackson jax-rs