使用 JAX-RS 和 Liferay (Apache CXF) 实现 PATCH 端点
Implement PATCH endpoint with JAX-RS and Liferay (Apache CXF)
我正在尝试在 Liferay OSGi 模块中使用 JAX-RS 实现 PATCH 端点。 GET、POST 和 PUT 端点工作正常,但我坚持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;
}
我正在尝试在 Liferay OSGi 模块中使用 JAX-RS 实现 PATCH 端点。 GET、POST 和 PUT 端点工作正常,但我坚持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;
}