61.6. 实体读者
概述 复制链接链接已复制到粘贴板!
本节介绍如何实施和注册 实体读取拦截器,这可让您在客户端或服务器端读取消息正文时截获输入流。这通常对请求正文的通用转换(如加密和解密)或压缩和解压缩非常有用。
ReaderInterceptor 接口 复制链接链接已复制到粘贴板!
javax.ws.rs.ext.ReaderInterceptor 接口定义如下:
// Java
...
package javax.ws.rs.ext;
public interface ReaderInterceptor {
public Object aroundReadFrom(ReaderInterceptorContext context)
throws java.io.IOException, javax.ws.rs.WebApplicationException;
}
通过实施 ReaderInterceptor 接口,您可以截获消息正文(实体 对象)在服务器端读取或客户端。您可以在以下任一上下文中使用实体读取器:
- 服务器端- 如果作为服务器端拦截器,则实体读取器在应用程序代码访问时截获请求消息正文(在匹配的资源中)。根据 REST 请求的语义,相关正文可能无法由匹配的资源访问,在这种情况下,读者拦截器没有调用。
-
客户端侧- 如果作为客户端拦截器,则实体读取器拦截器会在客户端代码访问时截获响应消息正文。如果客户端代码没有明确访问响应消息(例如,通过调用
Response.getEntity方法),则 reader 拦截器不会被调用。
ReaderInterceptorContext 接口 复制链接链接已复制到粘贴板!
ReaderInterceptor 的 aroundReadFrom 方法接收一条类型为 javax.ws.rs.ext.ReaderInterceptorContext 的参数,它可用于访问消息正文(实体 对象)和消息元数据。
ReaderInterceptorContext 接口定义如下:
// Java
...
package javax.ws.rs.ext;
import java.io.IOException;
import java.io.InputStream;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MultivaluedMap;
public interface ReaderInterceptorContext extends InterceptorContext {
public Object proceed() throws IOException, WebApplicationException;
public InputStream getInputStream();
public void setInputStream(InputStream is);
public MultivaluedMap<String, String> getHeaders();
}
InterceptorContext 接口 复制链接链接已复制到粘贴板!
ReaderInterceptorContext 接口还支持从基础 InterceptorContext 接口继承的方法。
InterceptorContext 接口定义如下:
// Java
...
package javax.ws.rs.ext;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collection;
import javax.ws.rs.core.MediaType;
public interface InterceptorContext {
public Object getProperty(String name);
public Collection<String> getPropertyNames();
public void setProperty(String name, Object object);
public void removeProperty(String name);
public Annotation[] getAnnotations();
public void setAnnotations(Annotation[] annotations);
Class<?> getType();
public void setType(Class<?> type);
Type getGenericType();
public void setGenericType(Type genericType);
public MediaType getMediaType();
public void setMediaType(MediaType mediaType);
}
客户端实现示例 复制链接链接已复制到粘贴板!
要为客户端实施实体读取器,请定义一个实施 ReaderInterceptor 接口的类。
例如,以下代码显示了客户端(优先级 10)的实体读取器拦截器示例,它替代了进入响应的消息正文中的所有 COMPANY_NAME 实例:
// Java
package org.jboss.fuse.example;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Priority;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.ext.ReaderInterceptor;
import javax.ws.rs.ext.ReaderInterceptorContext;
@Priority(value = 10)
public class SampleClientReaderInterceptor implements ReaderInterceptor {
@Override
public Object aroundReadFrom(ReaderInterceptorContext interceptorContext)
throws IOException, WebApplicationException
{
InputStream inputStream = interceptorContext.getInputStream();
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
String responseContent = new String(bytes);
responseContent = responseContent.replaceAll("COMPANY_NAME", "Red Hat");
interceptorContext.setInputStream(new ByteArrayInputStream(responseContent.getBytes()));
return interceptorContext.proceed();
}
}
服务器端实现示例 复制链接链接已复制到粘贴板!
要为服务器端实施实体读取器拦截器,请定义一个实施 ReaderInterceptor 接口的类,并使用 @Provider 注释标注。
例如,以下代码显示了服务器端的实体读取器拦截器(优先级为 10),它替代了传入请求消息正文中的所有 COMPANY_NAME 实例:
// Java
package org.jboss.fuse.example;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Priority;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.ReaderInterceptor;
import javax.ws.rs.ext.ReaderInterceptorContext;
@Priority(value = 10)
@Provider
public class SampleServerReaderInterceptor implements ReaderInterceptor {
@Override
public Object aroundReadFrom(ReaderInterceptorContext interceptorContext)
throws IOException, WebApplicationException {
InputStream inputStream = interceptorContext.getInputStream();
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
String requestContent = new String(bytes);
requestContent = requestContent.replaceAll("COMPANY_NAME", "Red Hat");
interceptorContext.setInputStream(new ByteArrayInputStream(requestContent.getBytes()));
return interceptorContext.proceed();
}
}
在客户端上绑定读者拦截器 复制链接链接已复制到粘贴板!
使用 JAX-RS 2.0 客户端 API,您可以直接在 javax.ws.rs.client.client.Client 对象或 对象上注册实体读取器。同样,这意味着读者可以选择性地应用到不同的范围,因此只有某些 URI 路径受拦截器影响。
javax. ws.rs.client.WebTarget
例如,以下代码演示了如何注册 SampleClientReaderInterceptor 拦截器,使其适用于使用 client 对象进行的所有调用:
// Java
...
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
...
Client client = ClientBuilder.newClient();
client.register(SampleClientReaderInterceptor.class);
有关使用 JAX-RS 2.0 客户端注册拦截器的详情,请参考 第 49.5 节 “配置客户端端点”。
在服务器端绑定读器 复制链接链接已复制到粘贴板!
要在服务器端 绑定 读取器(即,将其安装到 Apache CXF 运行时),请执行以下步骤:
将
@Provider注释添加到 reader 拦截器类,如以下代码片段所示:// Java package org.jboss.fuse.example; ... import javax.annotation.Priority; import javax.ws.rs.WebApplicationException; import javax.ws.rs.ext.Provider; import javax.ws.rs.ext.ReaderInterceptor; import javax.ws.rs.ext.ReaderInterceptorContext; @Priority(value = 10) @Provider public class SampleServerReaderInterceptor implements ReaderInterceptor { ... }当读取拦截器实现加载到 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="interceptorProvider" /> </jaxrs:providers> <bean id="interceptorProvider" class="org.jboss.fuse.example.SampleServerReaderInterceptor"/> </jaxrs:server> </blueprint>注意这一步是 Apache CXF 的非标准要求。根据 JAX-RS 标准严格讲,
@Provider注释应是绑定拦截器所必需的。但在实践中,标准方法有些不灵活,当大型项目中纳入多个库时,可能会导致供应商冲突。