61.6. Entity Reader Interceptor
概述
本节介绍如何在客户端或服务器端读取消息正文时,实施和注册 实体阅读器拦截器。这通常可用于请求正文的通用转换,如加密和解密、压缩和解压缩。
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 请求的语义,消息正文可能无法被匹配资源访问,在这种情况下,读取器不会被调用。
-
client side - 如果作为客户端侧拦截器绑定,实体读取器在客户端代码访问时截获响应消息正文。如果客户端代码没有显式访问响应消息(例如,通过调用
Response.getEntity
方法),则读取器拦截器不会被调用。
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
对象或 javax.ws.rs.client.WebTarget
对象上直接注册实体 reader 拦截器。这表示,读者拦截器可以选择性地应用到不同的范围,以便只有特定的 URI 路径会受到拦截器的影响。
例如,以下代码演示了如何注册 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 服务器端点”)时,将 reader 拦截器添加到
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
注释应全部是绑定拦截器所必需的。但是在实践中,标准方法有些不灵活,当许多库包含在大型项目中时,可能会导致禁止提供程序。