使用 JAX-RS 和 Liferay (Apache CXF) 实现 PATCH 端点

Implement PATCH endpoint with JAX-RS and Liferay (Apache CXF)

我正在尝试在 Liferay OSGi 模块中使用 JAX-RS 实现 PATCH 端点。 GETPOSTPUT 端点工作正常,但我坚持PATCH 端点。由于我不知道更好,我正在尝试使用 Daan Scheerens 的 example implementation

到目前为止我的(简化的)实现,从控制器开始:

@Path("/resources")
public class ResourceController {

    @PATCH
    @Path("/{resourceId}")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    Response patchResource(@PathParam("resourceId") long resourceId, ObjectPatch objectPatch) {

        // Get the resource
        Resource resource = getResource(resourceId);

        // Apply the patch
        objectPatch.apply(resource);

        // Return the resource
        return Response.ok(resource).build();
    }
}

所以我需要一个 ObjectPatch 界面,我在 Daan 的示例中完全一样:

public interface ObjectPatch {

    <T> T apply(T target) throws ObjectPatchException;

}

下一步是实现 MessageBodyReader:

@Provider
public class PartialJsonObjectPatchReader implements MessageBodyReader<ObjectPatch> {

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    @Override
    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {

        return ObjectPatch.class == type && MediaType.APPLICATION_JSON_TYPE.isCompatible(mediaType);
    }

    @Override
    public ObjectPatch readFrom(Class<ObjectPatch> type, Type genericType, Annotation[] annotations,
            MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
            throws IOException {

        JsonNode patch = OBJECT_MAPPER.readTree(entityStream);

        return new PartialJsonObjectPatch(OBJECT_MAPPER, patch);
    }

}

与示例实现的唯一区别是我添加了 @Provider 注释。据我了解,这会将 MessageBodyReader 实现自动注册到 JAX-RS 运行时,就像 here and here 所描述的那样。来自后者:

A class wishing to provide such a service implements the MessageBodyReader interface and may be annotated with @Provider for automatic discovery.

我只是觉得这种自动发现不会发生。

最后一个重要的class是ObjectPatch接口的实现:

public class PartialJsonObjectPatch implements ObjectPatch {

    private final ObjectMapper objectMapper;
    private final JsonNode patch;

    public PartialJsonObjectPatch(ObjectMapper objectMapper, JsonNode patch) {

        this.objectMapper = objectMapper;
        this.patch = patch;
    }

    @Override
    public <T> T apply(T target) throws ObjectPatchException {

        ObjectReader reader = objectMapper.readerForUpdating(target);

        try {
            return reader.readValue(patch);
        } catch (IOException e) {
            throw new ObjectPatchException(e);
        }
    }

}

如果我现在对端点执行 PATCH 请求,它会给我以下错误消息:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of com.example.ObjectPatch (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information

我将错误消息理解为无法实例化接口,但我不明白为什么 我会收到此消息。它不应该尝试实例化 PartialJsonObjectPatch 而不是 ObjectPatch 吗?如果我将 patchResource() 方法的参数 class 更改为 PartialJsonObjectPatch(我不应该这样做),我会收到此错误消息:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of com.example.PartialJsonObjectPatch (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

错误消息将我引导至 this,但添加默认构造函数没有帮助。

我错过了什么?

@Provider 注释是不够的,我不得不将我的 MessageBodyReader 实现添加到应用程序 class:

中的单例中
    @Override
    public Set<Object> getSingletons() {

        Set<Object> singletons = new HashSet<>();

        // All your other Providers, Readers, Writers, e.g:
        singletons.add(new JacksonJaxbJsonProvider());

        // My MessageBodyReader implementation
        singletons.add(new PartialJsonObjectPatchReader());

        return singletons;
    }