61.2. 容器请求过滤器
概述
本节介绍如何实施和注册容器请求 过滤器,该过滤器 用于截获 server (容器)端的传入请求消息。容器请求过滤器通常用于处理服务器端的标头,并可用于任何类型的通用请求处理(即处理独立于特定资源方法的处理)。
此外,容器请求过滤器是特殊情况的一部分,因为它可以在两个不同的扩展点上安装: PreMatchContainerRequest (资源匹配步骤前)和 ContainerRequest (资源匹配步骤之后)。
ContainerRequestFilter 接口
javax.ws.rs.container.ContainerRequestFilter 接口定义如下:
// Java
...
package javax.ws.rs.container;
import java.io.IOException;
public interface ContainerRequestFilter {
public void filter(ContainerRequestContext requestContext) throws IOException;
}
通过实现 ContainerRequestFilter 接口,您可以在服务器端为以下扩展点创建一个过滤器:
-
PreMatchContainerRequest -
ContainerRequest
ContainerRequestContext 接口
ContainerRequestFilter 的过滤器 方法接收类型为 javax.ws.rs.container.ContainerRequestContext 的单个参数,它可用于访问传入的请求消息及其关联的元数据。ContainerRequestContext 接口定义如下:
// Java
...
package javax.ws.rs.container;
import java.io.InputStream;
import java.net.URI;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo;
public interface ContainerRequestContext {
public Object getProperty(String name);
public Collection getPropertyNames();
public void setProperty(String name, Object object);
public void removeProperty(String name);
public UriInfo getUriInfo();
public void setRequestUri(URI requestUri);
public void setRequestUri(URI baseUri, URI requestUri);
public Request getRequest();
public String getMethod();
public void setMethod(String method);
public MultivaluedMap getHeaders();
public String getHeaderString(String name);
public Date getDate();
public Locale getLanguage();
public int getLength();
public MediaType getMediaType();
public List getAcceptableMediaTypes();
public List getAcceptableLanguages();
public Map getCookies();
public boolean hasEntity();
public InputStream getEntityStream();
public void setEntityStream(InputStream input);
public SecurityContext getSecurityContext();
public void setSecurityContext(SecurityContext context);
public void abortWith(Response response);
}PreMatchContainerRequest 过滤器的实现示例
要为 PreMatchContainerRequest 扩展点(即,在资源匹配之前执行过滤器的位置)实现容器请求过滤器,请定义一个实施 ContainerRequestFilter 接口的类,确保为类添加 @PreMatching 注解(选择 PreMatchContainerRequest 扩展点)。
例如,以下代码显示了在 PreMatchContainerRequest 扩展点中安装的简单容器请求过滤器示例,其优先级为 20:
// Java
package org.jboss.fuse.example;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.annotation.Priority;
import javax.ws.rs.ext.Provider;
@PreMatching
@Priority(value = 20)
@Provider
public class SamplePreMatchContainerRequestFilter implements
ContainerRequestFilter {
public SamplePreMatchContainerRequestFilter() {
System.out.println("SamplePreMatchContainerRequestFilter starting up");
}
@Override
public void filter(ContainerRequestContext requestContext) {
System.out.println("SamplePreMatchContainerRequestFilter.filter() invoked");
}
}ContainerRequest 过滤器的实现示例
要为 ContainerRequest 扩展点实施容器请求过滤器(即,过滤器在资源匹配 后执行 的位置),请定义一个实施 ContainerRequestFilter 接口的类,而无需 @PreMatching 注解。
例如,以下代码显示了在 ContainerRequest 扩展点中安装的简单容器请求过滤器示例,其优先级为 30 :
// Java
package org.jboss.fuse.example;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.ext.Provider;
import javax.annotation.Priority;
@Provider
@Priority(value = 30)
public class SampleContainerRequestFilter implements ContainerRequestFilter {
public SampleContainerRequestFilter() {
System.out.println("SampleContainerRequestFilter starting up");
}
@Override
public void filter(ContainerRequestContext requestContext) {
System.out.println("SampleContainerRequestFilter.filter() invoked");
}
}注入 ResourceInfo
在 ContainerRequest 扩展点(即发生资源匹配 后 ),可以通过注入 ResourceInfo 类来访问匹配的资源类和资源方法。例如,以下代码演示了如何注入 ResourceInfo 类作为 ContainerRequestFilter 类的字段:
// Java
package org.jboss.fuse.example;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.ext.Provider;
import javax.annotation.Priority;
import javax.ws.rs.core.Context;
@Provider
@Priority(value = 30)
public class SampleContainerRequestFilter implements ContainerRequestFilter {
@Context
private ResourceInfo resinfo;
public SampleContainerRequestFilter() {
...
}
@Override
public void filter(ContainerRequestContext requestContext) {
String resourceClass = resinfo.getResourceClass().getName();
String methodName = resinfo.getResourceMethod().getName();
System.out.println("REST invocation bound to resource class: " + resourceClass);
System.out.println("REST invocation bound to resource method: " + methodName);
}
}中止调用
通过创建合适的容器请求过滤器实施,可以中止服务器端调用。通常,这对在服务器端实施安全功能很有用:例如,为了实现身份验证功能或授权功能。如果传入的请求无法成功进行身份验证,您可以在容器请求过滤器内中止调用。
例如,以下预匹配功能尝试从 URI 的查询参数中提取用户名和密码,并调用验证方法来检查用户名和密码凭证。如果身份验证失败,则调用在 ContainerRequestContext 对象上的 abortWith 中止,传递要返回到客户端的错误响应。
// Java
package org.jboss.fuse.example;
import javax.annotation.Priority;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.Provider;
@PreMatching
@Priority(value = 20)
@Provider
public class SampleAuthenticationRequestFilter implements
ContainerRequestFilter {
public SampleAuthenticationRequestFilter() {
System.out.println("SampleAuthenticationRequestFilter starting up");
}
@Override
public void filter(ContainerRequestContext requestContext) {
ResponseBuilder responseBuilder = null;
Response response = null;
String userName = requestContext.getUriInfo().getQueryParameters().getFirst("UserName");
String password = requestContext.getUriInfo().getQueryParameters().getFirst("Password");
if (authenticate(userName, password) == false) {
responseBuilder = Response.serverError();
response = responseBuilder.status(Status.BAD_REQUEST).build();
requestContext.abortWith(response);
}
}
public boolean authenticate(String userName, String password) {
// Perform authentication of 'user'
...
}
}绑定服务器请求过滤器
要 绑定 服务器请求过滤器(即要将它安装到 Apache CXF 运行时中),请执行以下步骤:
将
@Provider注释添加到容器请求过滤器类,如以下代码片段所示:// Java package org.jboss.fuse.example; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.ext.Provider; import javax.annotation.Priority; @Provider @Priority(value = 30) public class SampleContainerRequestFilter implements ContainerRequestFilter { ... }
当容器请求过滤器实施加载到 Apache CXF 运行时中时,REST 实施会自动扫描加载的类,以搜索标有
@Provider注释( 扫描阶段)。在 XML 中定义 JAX-RS 服务器端点(例如,请参阅 第 18.1 节 “配置 JAX-RS 服务器端点”)时,将服务器请求过滤器添加到
jaxrs:providers元素中的供应商列表中。<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs" xmlns:cxf="http://cxf.apache.org/blueprint/core" ... > ... <jaxrs:server id="customerService" address="/customers"> ... <jaxrs:providers> <ref bean="filterProvider" /> </jaxrs:providers> <bean id="filterProvider" class="org.jboss.fuse.example.SampleContainerRequestFilter"/> </jaxrs:server> </blueprint>注意此步骤是 Apache CXF 的非标准要求。根据 JAX-RS 标准,严格说,
@Provider注释应当是绑定过滤器所需的所有内容。但是在实践中,标准方法有些不灵活,当许多库包含在大型项目中时,可能会导致禁止提供程序。