我正在阅读https://jersey.github.io/documentation/latest/filters-and-interceptors.html和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
是标题中的完整User-Agent字符串。对于我传入的每个视图:
AppUser.getName(userData), AppUser.isMobile(userAgent)
getName
函数解析json并仅返回name字段,isMobile
函数返回true布尔值,如果字符串" mobile"找到了。
我在FreeMarker中的应用程序的每个视图中使用它来显示用户的名称,并在移动设备为真时更改一些布局内容。
有没有办法减少重复次数?我宁愿使用BeforeFilter每次都自动设置它。
答案 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