61.7. 实体作者 Interceptor

概述

本节介绍如何实施和注册 实体作者,它可让您在客户端或服务器端写入消息正文时截获输出流。这通常对请求正文的通用转换(如加密和解密)或压缩和解压缩非常有用。

WriterInterceptor 接口

javax.ws.rs.ext.WriterInterceptor 接口定义如下:

// Java
...
package javax.ws.rs.ext;

public interface WriterInterceptor {
    void aroundWriteTo(WriterInterceptorContext context)
            throws java.io.IOException, javax.ws.rs.WebApplicationException;
}

通过实施 WriterInterceptor 接口,您可以截获消息正文(实体 对象)在服务器端编写或客户端。您可以在以下任一上下文中使用实体写器:

  • 服务器端- 如果作为服务器端拦截器,实体写入器拦截器会在响应消息正文之前被截取并发回到客户端。
  • 客户端侧- 如果作为客户端拦截器,实体写入器拦截器会在请求消息正文被放入并发送到服务器前截获请求消息正文。

WriterInterceptorContext 接口

WriterInterceptoraroundWriteTo 方法接收一个类型为 javax.ws.rs.ext.WriterInterceptorContext 的参数,它可用于访问消息正文(实体 对象)和消息元数据。

WriterInterceptorContext 接口定义如下:

// Java
...
package javax.ws.rs.ext;

import java.io.IOException;
import java.io.OutputStream;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MultivaluedMap;

public interface WriterInterceptorContext extends InterceptorContext {

    void proceed() throws IOException, WebApplicationException;

    Object getEntity();

    void setEntity(Object entity);

    OutputStream getOutputStream();

    public void setOutputStream(OutputStream os);

    MultivaluedMap<String, Object> getHeaders();
}

InterceptorContext 接口

WriterInterceptorContext 接口还支持从基础 InterceptorContext 接口继承的方法。有关 InterceptorContext 的定义,请参阅 “InterceptorContext 接口”一节

客户端实现示例

要为客户端实施实体作者,请定义一个实施 WriterInterceptor 接口的类。

例如,以下代码显示了客户端(优先级 10)的实体写器拦截器示例,它会将额外行文本附加到传出请求的消息正文中:

// Java
package org.jboss.fuse.example;

import java.io.IOException;
import java.io.OutputStream;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
import javax.annotation.Priority;

@Priority(value = 10)
public class SampleClientWriterInterceptor implements WriterInterceptor {

  @Override
  public void aroundWriteTo(WriterInterceptorContext interceptorContext)
          throws IOException, WebApplicationException {
    OutputStream outputStream = interceptorContext.getOutputStream();
    String appendedContent = "\nInterceptors always get the last word in.";
    outputStream.write(appendedContent.getBytes());
    interceptorContext.setOutputStream(outputStream);

    interceptorContext.proceed();
  }
}

服务器端实现示例

要为服务器端实施实体写器拦截器,请定义一个实施 WriterInterceptor 接口的类,并将其标上 @Provider 注释。

例如,以下代码显示了服务器端的实体写器拦截器(优先级为 10),它会将额外行文本附加到传出请求的消息正文:

// Java
package org.jboss.fuse.example;

import java.io.IOException;
import java.io.OutputStream;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
import javax.annotation.Priority;

@Priority(value = 10)
@Provider
public class SampleServerWriterInterceptor implements WriterInterceptor {

  @Override
  public void aroundWriteTo(WriterInterceptorContext interceptorContext)
          throws IOException, WebApplicationException {
    OutputStream outputStream = interceptorContext.getOutputStream();
    String appendedContent = "\nInterceptors always get the last word in.";
    outputStream.write(appendedContent.getBytes());
    interceptorContext.setOutputStream(outputStream);

    interceptorContext.proceed();
  }
}

在客户端上绑定写入器

使用 JAX-RS 2.0 客户端 API,您可以直接在 javax.ws.rs.client.Client 对象或 javax.ws.rs.client.WebTarget 对象上注册实体写器拦截器。同样,这意味着 writer 拦截器可以选择性地应用到不同的范围,因此只有某些 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 运行时),请执行以下步骤:

  1. @Provider 注释添加到 writer 拦截器类,如以下代码片段所示:

    // Java
    package org.jboss.fuse.example;
    ...
    import javax.ws.rs.WebApplicationException;
    import javax.ws.rs.ext.Provider;
    import javax.ws.rs.ext.WriterInterceptor;
    import javax.ws.rs.ext.WriterInterceptorContext;
    import javax.annotation.Priority;
    
    @Priority(value = 10)
    @Provider
    public class SampleServerWriterInterceptor implements WriterInterceptor {
      ...
    }

    当 writer 拦截器实现加载到 Apache CXF 运行时,REST 实现会自动扫描加载的类以搜索标有 @Provider 注释( 扫描阶段)的类。

  2. 在 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.SampleServerWriterInterceptor"/>
    
        </jaxrs:server>
    
    </blueprint>
    注意

    这一步是 Apache CXF 的非标准要求。根据 JAX-RS 标准严格讲,@Provider 注释应是绑定拦截器所必需的。但在实践中,标准方法有些不灵活,当大型项目中纳入多个库时,可能会导致供应商冲突。