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

Jersey filter in Dropwizard to set some global FreeMarker variables

我正在阅读 https://jersey.github.io/documentation/latest/filters-and-interceptors.html and http://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 是来自 [=31= 的完整 User-Agent 字符串].对于我传入的每个视图:

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

getName 函数解析 json 和 returns 只是名称字段和 isMobile 函数 returns 如果字符串 [=找到 29=]。

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

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

听起来像是您可以在 ContainerResponseFilter 中执行的操作,它在视图 resource/controller 的 return 之后 被调用。假设您正在 returning 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注解,用于决定哪些资源classes或方法应该通过这个过滤器。由于您不希望所有端点都通过此过滤器,因此只需注释您想要的方法或 classes。

@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 进行一些预处理,并且 header 调用资源方法之前,您可以在 ContainerRequestFilter 中执行此操作,也可以是名称绑定。而不是在响应过滤器中重新计算 AppUser.xxx 方法,您也可以只在 ContainerRequestContext#setProperty 上设置一个 属性,稍后您可以从相同的上下文中检索它 (getProperty)在响应过滤器中。

更新

上面的答案假设你使用的是Jersey's MVC support, hence the use of Viewable. If you are using Dropwizard's view support,那么它并没有太大的不同。您可能希望为所有视图 classes 创建一个抽象 class 作为 parent,这样您就可以在从过滤器检索实体时转换为抽象类型。

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

已测试资源 class 的完整性

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