input发送a.jax
PATCH方法是最不受欢迎的HTTP方法之一,因为直到最近才真正没有一种标准的PATCH格式。 一段时间以来,它已经针对JSON进行了标准化,因此有很多库可以为您完成繁重的工作。 出于本博客的目的,我将使用json-patch,尽管可以很容易地将此特定实现适应您选择的补丁库。
每个法线可以让资源和Bean类不受干扰。 在此示例代码中,我们有一个简单的资源,它知道如何返回原始对象,并且该资源允许您执行PATCH方法。 请注意,patch方法仅接受bean对象,这是因为我们需要做一些魔术才能预处理补丁。
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("service")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class Service {
@GET
public Bean get() {
return new Bean(true);
}
@PATCH
@Consumes("application/json-patch+json")
public Bean patch(Bean input) {
System.out.println(input.getMessage() + " " + input.getTitle());
return input;
}
}
import java.util.ArrayList;
import java.util.List;
public class Bean {
private String title = "title";
private String message = "message";
private List<String> list = new ArrayList<String>();
public Bean() {
this(false);
}
public Bean(boolean init) {
if (init) {
title = "title";
message = "message";
list.add("one");
list.add("two");
}
}
public void setList(List list) {
this.list = list;
}
public List getList() {
return list;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public void setMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
因此,对于这个示例,我们必须创建@PATCH
注释,幸运的是,JAX-RS为此目的包含了一个扩展元注释。 我们还将使用@NameBinding
因为此示例使用的是JAX-RS 2.0,因此我们可以@NameBinding
连接过滤器。
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.NameBinding;
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod("PATCH")
@Documented
@NameBinding
public @interface PATCH {
}
因此,这里是ReaderInterceptor
的实现,该实现将处理传入的流并将其替换为修补版本。 请注意,还使用@PATCH
对该类进行了注释,以使@NamedBinding
魔术起作用,并且还缺少许多错误处理,因为这是一个简单的POC。
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.fge.jsonpatch.JsonPatch;
import com.github.fge.jsonpatch.JsonPatchException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import javax.ws.rs.GET;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.ReaderInterceptor;
import javax.ws.rs.ext.ReaderInterceptorContext;
import org.glassfish.jersey.message.MessageBodyWorkers;
@Provider
@PATCH
public class PatchReader implements ReaderInterceptor {
private UriInfo info;
private MessageBodyWorkers workers;
@Context
public void setInfo(UriInfo info) {
this.info = info;
}
@Context
public void setWorkers(MessageBodyWorkers workers) {
this.workers = workers;
}
@Override
public Object aroundReadFrom(
ReaderInterceptorContext readerInterceptorContext)
throws IOException,
WebApplicationException {
// Get the resource we are being called on,
// and find the GET method
Object resource = info.getMatchedResources().get(0);
Method found = null;
for (Method next : resource.getClass().getMethods()) {
if (next.getAnnotation(GET.class) != null) {
found = next;
break;
}
}
if (found != null) {
// Invoke the get method to get the state we are trying to patch
//
Object bean;
try {
bean = found.invoke(resource);
} catch (Exception e) {
throw new WebApplicationException(e);
}
// Convert this object to a an aray of bytes
ByteArrayOutputStream baos = new ByteArrayOutputStream();
MessageBodyWriter<? super Object> bodyWriter =
workers.getMessageBodyWriter(Object.class, bean.getClass(),
new Annotation[0], MediaType.APPLICATION_JSON_TYPE);
bodyWriter.writeTo(bean, bean.getClass(), bean.getClass(),
new Annotation[0], MediaType.APPLICATION_JSON_TYPE,
new MultivaluedHashMap<String, Object>(), baos);
// Use the Jackson 2.x classes to convert both the incoming patch
// and the current state of the object into a JsonNode / JsonPatch
ObjectMapper mapper = new ObjectMapper();
JsonNode serverState = mapper.readValue(baos.toByteArray(),
JsonNode.class);
JsonNode patchAsNode = mapper.readValue(
readerInterceptorContext.getInputStream(),
JsonNode.class);
JsonPatch patch = JsonPatch.fromJson(patchAsNode);
try {
// Apply the patch
JsonNode result = patch.apply(serverState);
// Stream the result & modify the stream on the readerInterceptor
ByteArrayOutputStream resultAsByteArray =
new ByteArrayOutputStream();
mapper.writeValue(resultAsByteArray, result);
readerInterceptorContext.setInputStream(
new ByteArrayInputStream(
resultAsByteArray.toByteArray()));
// Pass control back to the Jersey code
return readerInterceptorContext.proceed();
} catch (JsonPatchException e) {
throw new WebApplicationException(
Response.status(500).type("text/plain").entity(e.getMessage()).build());
}
} else {
throw new IllegalArgumentException("No matching GET method on resource");
}
}
}
因此,一旦部署了此功能,就可以开始处理数据,因此原始消息是:
{
"list" : [
"one",
"two"
],
"message" : "message",
"title" : "title"
}
因此,如果应用以下修补程序,则返回的结果是:
[
{
"op" : "replace",
"path" : "/message",
"value" : "otherMessage"
},
{
"op" : "add",
"path" : "/list/-",
"value" : "three"
}
]
{
"list" : [
"one",
"two",
"three"
],
"message" : "otherMessage",
"title" : "title"
}
此示例说明,遵循简单的编码模式并使用简单的注释,将PATCH支持添加到您的类中相对比较简单。 这样,由于实现可以仅委托给您现有的PUT方法,因此PATCH支持变得微不足道。
更新: Jersey团队的Mirsolav Fuksa提醒我,为了使此实现符合PATCH RFC,它应在客户端执行OPTIONS请求时提供Accept-Patch
标头。 您可以使用简单的CotnainerResponseFilter来做到这一点:
import java.io.IOException;
import java.util.Collections;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
@Provider
public class OptionsAcceptHeader implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
if ("OPTIONS".equals(requestContext.getMethod())) {
if (responseContext.getHeaderString("Accept-Patch")==null) {
responseContext.getHeaders().put(
"Accept-Patch", Collections.<Object>singletonList("application/json-patch+json"));
}
}
}
}
翻译自: https://www.javacodegeeks.com/2014/02/transparent-patch-support-in-jax-rs-2-0.html
input发送a.jax