【问题标题】:Jersey filter in Dropwizard to set some global FreeMarker variablesDropwizard 中的 Jersey 过滤器以设置一些全局 FreeMarker 变量
【发布时间】:2018-02-03 02:28:44
【问题描述】:

我正在阅读 https://jersey.github.io/documentation/latest/filters-and-interceptors.htmlhttp://www.dropwizard.io/1.1.4/docs/manual/core.html#jersey-filters 来尝试做这个:

@CookieParam("User-Data") userData: String,
@HeaderParam("User-Agent") userAgent: String,

我的网络应用程序的每个资源 GET 方法都不需要。 userData 是来自 cookie 的 json 数据,其中包含“name”和“id”等字段,userAgent 是来自标头的完整 User-Agent 字符串。对于我传入的每个视图:

AppUser.getName(userData), AppUser.isMobile(userAgent)

getName 函数解析 json 并仅返回名称字段,如果找到字符串“mobile”,isMobile 函数返回一个真正的布尔值。

我在 FreeMarker 中应用程序的每个视图中都使用它来显示用户名并在移动设备为 true 时更改一些布局。

有没有办法减少重复?我宁愿每次都使用 BeforeFilter 来自动设置它。

【问题讨论】:

    标签: java jersey freemarker dropwizard


    【解决方案1】:

    听起来像是你可以在ContainerResponseFilter 中做的事情,它在视图资源/控制器返回后被调用。假设您返回一个Viewable,您从ContainerRequestContext#getEntity 中获取Viewable,从中获取模型,并将额外信息添加到模型中。

    @Provider
    @UserInModel
    public class UserInModelFilter implements ContainerResponseFilter {
    
        @Override
        public void filter(ContainerRequestContext request,
                           ContainerResponseContext response) throws IOException {
    
            Cookie cookie = request.getCookies().get("User-Data");
            String header =  request.getHeaderString("User-Agent");
    
            String username = AppUser.getName(cookie.getValue());
            boolean isMobile = AppUser.isMobile(header);
    
            Viewable returnViewable =  (Viewable) response.getEntity();
            Map<String, Object> model = (Map<String, Object>) returnViewable.getModel();
    
            model.put("username", username);
            model.put("isMobile", isMobile);
        }
    }
    

    @UserInModel注解是自定义的Name Binding注解,用于确定哪些资源类或方法应该通过这个过滤器。由于您不希望所有端点都通过此过滤器,因此只需注释您想要的方法或类。

    @NameBinding
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD})
    public @interface UserInModel {
    }
    
    
    @Path("/")
    public class IndexController {
    
        @GET
        @UserInModel
        @Produces(MediaType.TEXT_HTML)
        public Viewable home() {
            Map<String, Object> model = new HashMap<>();
            return new Viewable("/index", model);
        }
    }
    

    使用 Dropwizard,您只需注册过滤器即可。

    env.jersey().register(UserInModelFilter.class);
    

    如果您想在调用资源方法之前对cookie 和标头进行一些预处理,您可以在ContainerRequestFilter 中进行,这也可以是名称绑定。除了在响应过滤器中重新计算 AppUser.xxx 方法之外,您还可以在 ContainerRequestContext#setProperty 上设置一个属性,以后可以在响应过滤器中从同一上下文 (getProperty) 中检索该属性。

    更新

    以上答案假设您使用的是Jersey's MVC support,因此使用Viewable。如果您使用的是Dropwizard's view support,那么并没有太大的不同。您可能希望创建一个抽象类作为所有视图类的父类,这样您就可以在从过滤器中检索实体时强制转换为抽象类型。

    public class AbstractView extends View {
    
        private String userName;
        private boolean isMobile;
    
        protected AbstractView(String templateName) {
            super(templateName);
        }
    
        public String getUserName() { return userName; }
    
        public void setUserName(String userName) { this.userName = userName; }
    
        public boolean isMobile() { return isMobile; }
    
        public void setIsMobile(boolean mobile) { isMobile = mobile; }
    }
    
    public class PersonView extends AbstractView {
    
        private final Person person;
    
        public PersonView(Person person) {
            super("person.ftl");
            this.person = person;
        }
    
        public Person getPerson() {
            return this.person;
        }
    }
    

    在过滤器中

    @Provider
    @UserInModel
    public class UserInModelFilter implements ContainerResponseFilter {
    
        @Override
        public void filter(ContainerRequestContext request,
                           ContainerResponseContext response) throws IOException {
    
            Cookie cookie = request.getCookies().get("User-Data");
            String header =  request.getHeaderString("User-Agent");
    
            String username = AppUser.getName(cookie.getValue());
            boolean isMobile = AppUser.isMobile(header);
    
            AbstractView returnViewable =  (AbstractView) response.getEntity();
            returnViewable.setUserName(username);
            returnViewable.setIsMobile(isMobile);
        }
    }
    

    测试资源类的完整性

    @Path("person")
    public class PersonController {
    
        @GET
        @UserInModel
        @Produces(MediaType.TEXT_HTML)
        public PersonView person() {
            Person person = new Person("peeskillet@fake.com");
            return new PersonView(person);
        }
    }
    

    【讨论】:

    • 这太棒了!谢谢,你知道 Viewable 来自哪个包吗?
    • org.glassfish.jersey.server.mvc.Viewable。你没有吗?我正在使用Jersey's MVC support to test。我假设这就是你正在使用的。
    • 我猜你正在使用Dropwizard's view support。我不认为他们使用 Jersey 的 MVC。您可能仍然可以这样做,而不是获取Viewable,只需从过滤器内部获取View
    • 可能创建一个AbstractView,所有视图都从该AbstractView 扩展,您可以在其中设置 user 和 isMobile 属性。这样你就可以从过滤器中获取 AbstractView。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-21
    • 2012-12-26
    • 1970-01-01
    • 2013-01-19
    相关资源
    最近更新 更多