我设法想出了一个解决方法。我决定创建一个 ResourceFilter、对应的 ResourceFilterFactory 和一个注释类型,而不是声明要在 Jersey 资源方法上使用哪个 ObjectMapper。每当使用此类型注释资源类或方法时,ResourceFilter 会将请求的 Content-Type 覆盖为注释参数中声明的任何内容。
这是我的代码:
OverrideInputType注解:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OverrideInputType {
// What the Content-Type request header value should be replaced by
String value();
// which Content-Type request header values should not be replaced
String[] except() default {};
}
OverrideInputTypeResourceFilter:
public class OverrideInputTypeResourceFilter implements ResourceFilter, ContainerRequestFilter {
private MediaType targetType;
private Set<MediaType> exemptTypes;
OverrideInputTypeResourceFilter(
@Nonnull String targetType,
@Nonnull String[] exemptTypes
) {
this.targetType = MediaType.valueOf(targetType);
this.exemptTypes = new HashSet<MediaType>(Lists.transform(
Arrays.asList(exemptTypes),
exemptType -> MediaType.valueOf(exemptType)
));
}
@Override
public ContainerRequest filter(ContainerRequest request) {
MediaType inputType = request.getMediaType();
if (targetType.equals(inputType) || exemptTypes.contains(inputType)) {
// unmodified
return request;
}
MultivaluedMap<String, String> headers = request.getRequestHeaders();
if (headers.containsKey(HttpHeaders.CONTENT_TYPE)) {
headers.putSingle(HttpHeaders.CONTENT_TYPE, targetType.toString());
request.setHeaders((InBoundHeaders)headers);
}
return request;
}
@Override
public final ContainerRequestFilter getRequestFilter() {
return this;
}
@Override
public final ContainerResponseFilter getResponseFilter() {
// don't filter responses
return null;
}
}
OverrideInputTypeResourceFilterFactory:
public class OverrideInputTypeResourceFilterFactory implements ResourceFilterFactory {
@Override
public List<ResourceFilter> create(AbstractMethod am) {
// documented to only be AbstractSubResourceLocator, AbstractResourceMethod, or AbstractSubResourceMethod
if (am instanceof AbstractSubResourceLocator) {
// not actually invoked per request, nothing to do
log.debug("Ignoring AbstractSubResourceLocator {}", am);
return null;
} else if (am instanceof AbstractResourceMethod) {
OverrideInputType annotation = am.getAnnotation(OverrideInputType.class);
if (annotation == null) {
annotation = am.getResource().getAnnotation(OverrideInputType.class);
}
if (annotation != null) {
return Lists.<ResourceFilter>newArrayList(
new OverrideInputTypeResourceFilter(annotation.value(), annotation.except()));
}
} else {
log.warn("Got an unexpected instance of {}: {}", am.getClass().getName(), am);
}
return null;
}
}
示例MyResource 演示其用法:
@Produces(MediaType.APPLICATION_JSON)
@Path(/objects/{id}")
public class MyResource {
@PUT
// @Consumes(MediaType.APPLICATION_JSON)
@OverrideInputType(MediaType.APPLICATION_JSON)
public StatusResult put(@PathParam("id") int id, JsonObject obj) {
log.trace("PUT {}", id);
// do something with obj
return new StatusResult(true);
}
@GET
public JsonObject get(@PathParam("id") int id) {
return new JsonObject(id);
}
}
在 Jersey 2 中,您可以使用后匹配 ContainerRequestFilters 来做到这一点