Dropwizard中的Jersey过滤器设置一些全局FreeMarker变量

时间:2017-08-25 04:49:59

标签: java jersey freemarker dropwizard

我正在阅读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并仅返回name字段,isMobile函数返回true布尔值,如果字符串" mobile"找到了。

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

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

1 个答案:

答案 0 :(得分:7)

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

@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上设置一个属性,以后可以从响应过滤器中的相同上下文(Viewable)中检索该属性

更新

上述答案假设您使用Jersey's MVC 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; } } 。如果您使用的是Dropwizard's view support,那么它并没有太大的不同。您可能希望创建一个抽象类作为所有视图类的父类,这样您就可以在从过滤器中检索实体时转换为抽象类型。

@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);
    }
}

已完成测试的资源类

@foreach($obj->posts as $post)
   <h1>$post->title</h1>
   <p>$post->content</p>
@endforeach