当前位置: 首页 > 知识库问答 >
问题:

Jersey:硬编码POST/PUT对象映射器,无需内容类型头

公西苗宣
2023-03-14

我有一个泽西1.19.1资源,它实现了@PUT@POST方法。@PUT方法需要一个JSON字符串作为输入/请求正文,而@POST方法接受纯文本。对于JSON映射,我使用Jackson 2.8。

由于资源是按这种方式定义的,我不希望客户端需要指定内容类型请求头,因为Jersey需要它来确定在请求体上使用哪个对象映射器。

相反,我想告诉Jersey“将此对象映射器用于此输入”,或者“始终假定此输入将在此方法上具有一个应用程序/json内容类型”

@Produces(MediaType.APPLICATION_JSON)
@Path("/some/endpoint/{id}")
public class MyResource {

    @PUT
    public JsonResult put(
        @PathParam("id") String id,
        // this should always be deserialized by Jackson, regardless of the `Content-Type` request header.
        JsonInput input
    ) {
        log.trace("PUT {}, {}, {}", id, input.foo, input.bar);
        return new JsonResult("PUT result");
    }

    @POST
    public JsonResult post(
        @PathParam("id") String id,
        // this should always be treated as plain text, regardless of the `Content-Type` request header.
        String input
    ) {
        log.trace("POST {}, {}", id, input);
        return new JsonResult("POST result");
    }
}

我只找到了这个答案,但这不是我想要的,因为解决方案似乎是要求客户端添加正确的Content-Type标头,或者手动执行对象映射。

共有1个答案

农雅畅
2023-03-14

我设法想出了一个解决方法。我决定创建一个ResourceFilter、相应的ResourceFilterFactory和一个注释类型,而不是声明在泽西资源方法上使用哪个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 {};
}

覆盖InputTypeResourceFilter:

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;
    }
}

覆盖输入类型资源过滤器工厂

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中,您可以通过post匹配ContainerRequestFilters来实现这一点

 类似资料:
  • 我的服务器资源: 我的客户资源: 我的Build.Gradle文件:

  • 问题内容: hadoop的新手,并试图从此处了解mapreduce wordcount示例代码。 文档中的映射器是- 我看到在mapreduce字数示例中,映射代码如下 问题- Object类型的此键的作用是什么?如果映射器的输入是文本文档,那么我假设其中的值将是hadoop已分区并存储在HDFS中的文本块(64MB或128MB)。 更一般而言,此输入键Keyin在地图代码中的用途是什么? 任何指

  • 我有一个http:inbound-gateway,接收一个json消息,做一些充实,并使用http:outbound-gateway将它发送到一个endpoint,以调用一个同样具有json有效负载的rest服务。 入站gw接收内容类型标头,它设置在消息标头上,但当使用出站gw将有效负载发送到其余服务时,会发生以下错误:415不支持的媒体类型 我检查了日志,出现了以下警告: WARN Defaul

  • 我有一个对象,我正试图映射到。现在这个有一个名为的枚举,其中包含一些值。我想使用将它们映射到中的其他枚举值。以下是我到目前为止的代码: 当我尝试编译它时,我得到了错误:

  • 我的示例Jersey客户端代码是 我一直在尝试这个代码变体,从上一个星期以来,它是不工作的。在这方面的任何帮助都是高度赞赏的。

  • 我试图使用http://modelmapper.org/表示DAO和模型类的库- 模型类- 道类- 公共类主题{私有字符串名称; 映射逻辑 ModelMapper似乎不起作用,它给我提供了主题类项目,而不是主题模型类项目