在上一篇文章中 ,我们研究了使用MOXy的功能来控制特定实体的数据输出级别。 这篇文章着眼于Jersey 2.x提供的抽象,它允许您定义一组自定义的批注以具有相同的效果。
与之前一样,我们几乎没有什么琐碎的资源可以返回Jersey将为我们转换为JSON的对象,请注意,目前此代码中没有任何内容可以进行过滤-我不会将注释传递给
Response
对象,如Jersey示例中所示:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;@Path("hello")
public class SelectableHello {@GET@Produces({ "application/json; level=detailed", "application/json; level=summary", "application/json; level=normal" })public Message hello() {return new Message();}
}
在我的设计中,我将定义四个注释: NoView
, SummaryView
, NormalView
和DetailedView
。 所有根对象都必须实现NoView注释,以防止暴露未注释的字段-您可能觉得在设计中没有必要。 所有这些类看起来都一样,所以我只显示一个。 请注意,创建AnnotationLiteral
的工厂方法必须优先于创建动态代理以具有相同效果的工厂使用。 2.5中有代码,将忽略由java.lang.reflect.Proxy
对象实现的任何注释,其中包括您可能从类中检索到的所有注释。 我正在为此提交修复程序。
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.enterprise.util.AnnotationLiteral;import org.glassfish.jersey.message.filtering.EntityFiltering;@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EntityFiltering
public @interface NoView {/*** Factory class for creating instances of the annotation.*/public static class Factory extends AnnotationLiteral<NoView> implements NoView {private Factory() {}public static NoView get() {return new Factory();}}}
现在我们可以快速浏览一下Message Bean,这比我以前的示例稍微复杂一点,以非常简单的形式显示子图的过滤。 正如我之前说过的那样,在类的根部使用NoView注释进行注释–这应该意味着privateData
不会返回给客户端,因为它没有特别地注释。
import javax.xml.bind.annotation.XmlRootElement;@XmlRootElement
@NoView
public class Message {private String privateData;@SummaryViewprivate String summary;@NormalViewprivate String message;@DetailedViewprivate String subtext;@DetailedViewprivate SubMessage submessage;public Message() {summary = "Some simple summary";message = "This is indeed the message";subtext = "This is the deep and meaningful subtext";submessage = new SubMessage();privateData = "The fox is flying tonight";}// Getters and setters not shown
}public class SubMessage {private String message;public SubMessage() {message = "Some sub messages";}// Getters and setters not shown
}
如前所述,资源类中没有代码可以处理过滤–我认为这是一个横切关注点,因此我将其抽象为WriterInterceptor。 请注意,如果使用的实体上没有NoView批注,则会抛出该异常。
import java.io.IOException;import java.lang.annotation.Annotation;import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;import javax.ws.rs.ServerErrorException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;@Provider
public class ViewWriteInterceptor implements WriterInterceptor {private HttpHeaders httpHeaders;public ViewWriteInterceptor(@Context HttpHeaders httpHeaders) {this.httpHeaders = httpHeaders;}@Overridepublic void aroundWriteTo(WriterInterceptorContext writerInterceptorContext) throws IOException,WebApplicationException {// I assume this case will never happen, just to be sureif (writerInterceptorContext.getEntity() == null) {writerInterceptorContext.proceed();return;}else{Class<?> entityType = writerInterceptorContext.getEntity().getClass();String entityTypeString = entityType.getName();// Ignore any Jersey system classes, for example wadl//if (entityType == String.class || entityType.isArray() || entityTypeString.startsWith("com.sun") || entityTypeString.startsWith("org.glassfish")) {writerInterceptorContext.proceed();return;}// Fail if the class doesn't have the default NoView annotation // this prevents any unannotated fields from showing up//else if (!entityType.isAnnotationPresent(NoView.class)) {throw new ServerErrorException("Entity type should be tagged with @NoView annotation " + entityType, Response.Status.INTERNAL_SERVER_ERROR);}}// Get hold of the return media type://MediaType mt = writerInterceptorContext.getMediaType();String level = mt.getParameters().get("level");// Get the annotations and modify as required//Set<Annotation> current = new LinkedHashSet<>();current.addAll(Arrays.asList(writerInterceptorContext.getAnnotations()));switch (level != null ? level : "") {default:case "detailed":current.add(com.example.annotation.DetailedView.Factory.get());case "normal":current.add(com.example.annotation.NormalView.Factory.get());case "summary":current.add(com.example.annotation.SummaryView.Factory.get());}writerInterceptorContext.setAnnotations(current.toArray(new Annotation[current.size()]));//writerInterceptorContext.proceed();}
}
最后,您必须手动启用EntityFilterFeature,为此,您可以在Application类中简单地注册它
import java.lang.annotation.Annotation;import javax.ws.rs.ApplicationPath;import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
import org.glassfish.jersey.server.ResourceConfig;@ApplicationPath("/resources/")
public class SelectableApplication extends ResourceConfig {public SelectableApplication() {packages("...");// Set entity-filtering scope via configuration.property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, new Annotation[] {NormalView.Factory.get(), DetailedView.Factory.get(), NoView.Factory.get(), SummaryView.Factory.get()});register(EntityFilteringFeature.class);}}
一旦一切就绪并运行,应用程序将像以前一样响应:
GET .../hello Accept application/json; level=detailed or application/json
{"message" : "This is indeed the message","submessage" : {"message" : "Some sub messages"},"subtext" : "This is the deep and meaningful subtext","summary" : "Some simple summary"
}GET .../hello Accept application/json; level=normal
{"message" : "This is indeed the message","summary" : "Some simple summary"
}GET .../hello Accept application/json; level=summary
{"summary" : "Some simple summary"
}
这是直接使用MOXy批注的更好选择–使用自定义批注应该更容易将应用程序移植到过度实现中,即使您必须提供自己的过滤器也是如此。 最后,值得探讨的是Jersey扩展,它允许基于角色的筛选 ,我认为这在安全方面很有用。
翻译自: https://www.javacodegeeks.com/2014/02/selecting-level-of-detail-returned-by-varying-the-content-type-part-ii.html