搜索

第 51 章 实体支持

download PDF

摘要

Apache CXF 运行时支持 MIME 类型和 Java 对象之间的有限数量的映射。开发人员可以通过实施自定义读取器和写入器来扩展映射。自定义读取器和写入器在启动时使用运行时注册。

概述

运行时依赖于 JAX-RS MessageBodyReader 和 MessageBodyWriter 实现来序列化 HTTP 消息及其 Java 表示之间的数据。读取器和写入器可以限制他们能够处理的 MIME 类型。

运行时为多个常见映射提供读取器和写入器。如果应用程序需要更多的映射,开发人员可以提供 MessageBodyReader 接口和/或 MessageBodyWriter 接口的自定义实现。自定义读取器和写入器在应用启动时使用运行时注册。

原生支持的类型

表 51.1 “原生支持的实体映射” 列出 Apache CXF 提供的实体映射。

表 51.1. 原生支持的实体映射
Java 类型MIME 类型

原语类型

text/plain

java.lang.Number

text/plain

byte[]

*/*

java.lang.String

*/*

java.io.InputStream

*/*

java.io.Reader

*/*

java.io.File

*/*

javax.activation.DataSource

*/*

javax.xml.transform.Source

text/xml,application/xml,application/\*+xml

javax.xml.bind.JAXBElement

text/xml,application/xml,application/\*+xml

JAXB 注解的对象

text/xml,application/xml,application/\*+xml

javax.ws.rs.core.MultivaluedMap<String, String>

application/x-www-form-urlencoded [a]

javax.ws.rs.core.StreamingOutput

*/* [b]

[a] 此映射用于处理 HTML 表单数据。
[b] 这个映射只支持将数据返回到消费者。

自定义读取器

自定义实体读取器负责将传入的 HTTP 请求映射到服务实施可以操作的 Java 类型中。它们实施 javax.ws.rs.ext.MessageBodyReader 接口。

例 51.1 “消息读取器接口” 中显示的接口有两个需要实现的方法:

例 51.1. 消息读取器接口

package javax.ws.rs.ext;

public interface MessageBodyReader<T>
{
  public boolean isReadable(java.lang.Class<?> type,
                            java.lang.reflect.Type genericType,
                            java.lang.annotation.Annotation[] annotations,
                            javax.ws.rs.core.MediaType mediaType);

  public T readFrom(java.lang.Class<T> type,
                    java.lang.reflect.Type genericType,
                    java.lang.annotation.Annotation[] annotations,
                    javax.ws.rs.core.MediaType mediaType,
                    javax.ws.rs.core.MultivaluedMap<String, String> httpHeaders,
                    java.io.InputStream entityStream)
  throws java.io.IOException, WebApplicationException;
}
isReadable()

isReadable () 方法决定了读取器是否能够读取数据流,并创建正确的实体表示类型。如果读取器可以创建正确的实体类型,则方法返回为 true

表 51.2 “用于确定读取器是否可以生成实体的参数” 描述 isReadable () 方法的参数。

表 51.2. 用于确定读取器是否可以生成实体的参数
参数类型描述

type

类<T>

指定用于存储实体的对象的实际 Java 类。

genericType

类型

指定用于存储实体的对象的 Java 类型。例如,如果要将消息正文转换为 method 参数,则该值将是 Method.getGenericParameterTypes () 方法返回的 method 参数的类型。

annotations

Annotation[]

指定为存储实体而创建的对象声明的注解列表。例如,如果消息正文要转换为 method 参数,这将是 Method.getParameterAnnotations () 方法返回的该参数上的注解。

mediaType

MediatType

指定 HTTP 实体的 MIME 类型。

readFrom()

readFrom () 方法读取 HTTP 实体,并将其覆盖到所需的 Java 对象中。如果读取成功,则方法返回包含该实体的创建的 Java 对象。如果在读取输入流时发生错误,方法应抛出 IOException 异常。如果发生错误需要 HTTP 错误响应,应抛出带有 HTTP 响应的 WebApplicationException。

表 51.3 “用于读取实体的参数” 描述 readFrom () 方法的参数。

表 51.3. 用于读取实体的参数
参数类型描述

type

类<T>

指定用于存储实体的对象的实际 Java 类。

genericType

类型

指定用于存储实体的对象的 Java 类型。例如,如果要将消息正文转换为 method 参数,则该值将是 Method.getGenericParameterTypes () 方法返回的 method 参数的类型。

annotations

Annotation[]

指定为存储实体而创建的对象声明的注解列表。例如,如果消息正文要转换为 method 参数,这将是 Method.getParameterAnnotations () 方法返回的该参数上的注解。

mediaType

MediatType

指定 HTTP 实体的 MIME 类型。

httpHeaders

MultivaluedMap<String, String>

指定与实体关联的 HTTP 消息标头。

entityStream

InputStream

指定包含 HTTP 实体的输入流。

重要

这个方法不应关闭输入流。

在 MessageBodyReader 实现可用作实体阅读器之前,它必须使用 javax.ws.rs.ext.Provider 注解进行分离。@Provider 注释提醒提供的实施提供了额外功能的运行时。这个实现也必须使用运行时注册,如 “注册阅读器和作者”一节 所述。

默认情况下,自定义实体供应商处理所有 MIME 类型。您可以使用 javax.ws.rs.Consumes 注解限制自定义实体读取器将处理的 MIME 类型。@Consumes 注释指定自定义实体提供程序读取的、以逗号分隔的 MIME 类型列表。如果实体不是指定的 MIME 类型,则不会选择实体提供程序作为可能的读取器。

例 51.2 “XML 源实体读取器” 显示使用 XML 实体的实体读取器,并将它们存储在 Source 对象中。

例 51.2. XML 源实体读取器

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Consumes;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;

import org.w3c.dom.Document;
import org.apache.cxf.jaxrs.ext.xml.XMLSource;

@Provider
@Consumes({"application/xml", "application/*+xml", "text/xml", "text/html" })
public class SourceProvider implements MessageBodyReader<Object>
{
  public boolean isReadable(Class<?> type,
                            Type genericType,
                            Annotation[] annotations,
                            MediaType mt)
  {
    return Source.class.isAssignableFrom(type) || XMLSource.class.isAssignableFrom(type);
  }

  public Object readFrom(Class<Object> source,
                         Type genericType,
                         Annotation[] annotations,
                         MediaType mediaType,
                         MultivaluedMap<String, String> httpHeaders,
                         InputStream is)
  throws IOException
  {
    if (DOMSource.class.isAssignableFrom(source))
    {
      Document doc = null;
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      DocumentBuilder builder;
      try
      {
        builder = factory.newDocumentBuilder();
        doc = builder.parse(is);
      }
      catch (Exception e)
      {
        IOException ioex = new IOException("Problem creating a Source object");
        ioex.setStackTrace(e.getStackTrace());
        throw ioex;
       }

       return new DOMSource(doc);
    }
    else if (StreamSource.class.isAssignableFrom(source) || Source.class.isAssignableFrom(source))
    {
      return new StreamSource(is);
    }
    else if (XMLSource.class.isAssignableFrom(source))
    {
      return new XMLSource(is);
    }

    throw new IOException("Unrecognized source");
  }
}

自定义写入器

自定义实体作者负责将 Java 类型映射到 HTTP 实体。它们实施 javax.ws.rs.ext.MessageBodyWriter 接口。

例 51.3 “消息写入器接口” 中显示的接口有三个需要实现的方法:

例 51.3. 消息写入器接口

package javax.ws.rs.ext;

public interface MessageBodyWriter<T>
{
  public boolean isWriteable(java.lang.Class<?> type,
                             java.lang.reflect.Type genericType,
                             java.lang.annotation.Annotation[] annotations,
                             javax.ws.rs.core.MediaType mediaType);

  public long getSize(T t,
                      java.lang.Class<?> type,
                      java.lang.reflect.Type genericType,
                      java.lang.annotation.Annotation[] annotations,
                      javax.ws.rs.core.MediaType mediaType);

  public void writeTo(T t,
                      java.lang.Class<?> type,
                      java.lang.reflect.Type genericType,
                      java.lang.annotation.Annotation[] annotations,
                      javax.ws.rs.core.MediaType mediaType,
                      javax.ws.rs.core.MultivaluedMap<String, Object> httpHeaders,
                      java.io.OutputStream entityStream)
  throws java.io.IOException, WebApplicationException;
}
isWriteable()

isWriteable () 方法确定实体写入器是否可以将 Java 类型映射到正确的实体类型。如果写入器可以进行映射,则方法会返回 true

表 51.4 “用于读取实体的参数” 描述 isWritable () 方法的参数。

表 51.4. 用于读取实体的参数
参数类型描述

type

类<T>

指定正在写入对象的 Java 类。

genericType

类型

指定要编写的对象的 Java 类型,通过反映资源方法返回类型或通过检查返回的实例来获取。GenericEntity 类(如 第 48.4 节 “使用通用类型信息返回实体” 所述)提供对控制这个值的支持。

annotations

Annotation[]

指定返回实体的方法上的注解列表。

mediaType

MediatType

指定 HTTP 实体的 MIME 类型。

getSize()

getSize () 方法在 writeTo () 之前调用。它返回所写入实体的长度(以字节为单位)。如果返回正值,则该值将写入 HTTP 消息的 Content-Length 标头中。

表 51.5 “用于读取实体的参数” 描述 getSize () 方法的参数。

表 51.5. 用于读取实体的参数
参数类型描述

t

generic

指定正在写入的实例。

type

类<T>

指定正在写入对象的 Java 类。

genericType

类型

指定要编写的对象的 Java 类型,通过反映资源方法返回类型或通过检查返回的实例来获取。GenericEntity 类(如 第 48.4 节 “使用通用类型信息返回实体” 所述)提供对控制这个值的支持。

annotations

Annotation[]

指定返回实体的方法上的注解列表。

mediaType

MediatType

指定 HTTP 实体的 MIME 类型。

writeTo()

writeTo () 方法将 Java 对象转换为所需的实体类型,并将实体写入输出流。如果在将实体写入输出流时发生错误,则方法应抛出 IOException 异常。如果发生错误需要 HTTP 错误响应,应抛出带有 HTTP 响应的 WebApplicationException。

表 51.6 “用于读取实体的参数” 描述 writeTo () 方法的参数。

表 51.6. 用于读取实体的参数
参数类型描述

t

generic

指定正在写入的实例。

type

类<T>

指定正在写入对象的 Java 类。

genericType

类型

指定要编写的对象的 Java 类型,通过反映资源方法返回类型或通过检查返回的实例来获取。GenericEntity 类(如 第 48.4 节 “使用通用类型信息返回实体” 所述)提供对控制这个值的支持。

annotations

Annotation[]

指定返回实体的方法上的注解列表。

mediaType

MediatType

指定 HTTP 实体的 MIME 类型。

httpHeaders

MultivaluedMap<String, Object>

指定与实体关联的 HTTP 响应标头。

entityStream

OutputStream

指定将实体写入的输出流。

在 MessageBodyWriter 实现可用作实体写入器之前,它必须使用 javax.ws.rs.ext.Provider 注解进行分离。@Provider 注释提醒提供的实施提供了额外功能的运行时。这个实现也必须使用运行时注册,如 “注册阅读器和作者”一节 所述。

默认情况下,自定义实体供应商处理所有 MIME 类型。您可以使用 javax.ws.rs.Produces 注解限制自定义实体写器将处理的 MIME 类型。@Produces 注释指定自定义实体提供程序生成的以逗号分隔的 MIME 类型列表。如果实体不是指定的 MIME 类型,则不会选择实体提供程序作为可能的写器。

例 51.4 “XML 源实体编写器” 显示接受 Source 对象并生成 XML 实体的实体编写器。

例 51.4. XML 源实体编写器

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;

import org.apache.cxf.jaxrs.ext.xml.XMLSource;

@Provider
@Produces({"application/xml", "application/*+xml", "text/xml" })
public class SourceProvider implements MessageBodyWriter<Source>
{

  public boolean isWriteable(Class<?> type,
                             Type genericType,
                             Annotation[] annotations,
                             MediaType mt)
  {
    return Source.class.isAssignableFrom(type);
  }

  public void writeTo(Source source,
                      Class<?> clazz,
                      Type genericType,
                      Annotation[] annotations,
                      MediaType mediatype,
                      MultivaluedMap<String, Object> httpHeaders,
                      OutputStream os)
  throws IOException
  {
    StreamResult result = new StreamResult(os);
    TransformerFactory tf = TransformerFactory.newInstance();
    try
    {
      Transformer t = tf.newTransformer();
      t.transform(source, result);
    }
    catch (TransformerException te)
    {
      te.printStackTrace();
      throw new WebApplicationException(te);
    }
  }

  public long getSize(Source source,
                      Class<?> type,
                      Type genericType,
                      Annotation[] annotations,
                      MediaType mt)
  {
    return -1;
  }
}

注册阅读器和作者

在 JAX-RS 应用可以使用任何自定义实体提供程序之前,必须在运行时注册自定义提供程序。提供程序使用应用配置文件中的 jaxrs:providers 元素或使用 JAXRSServerFactoryBean 类通过运行时注册。

jaxrs:providers 元素是 jaxrs:server 元素的子级,包含 bean 元素的列表。每个 bean 元素定义一个实体提供程序。

例 51.5 “使用运行时注册实体供应商” 显示 JAX-RS 服务器,配置为使用一组自定义实体提供程序。

例 51.5. 使用运行时注册实体供应商

<beans ...>
  <jaxrs:server id="customerService" address="/">
    ...
    <jaxrs:providers>
      <bean id="isProvider" class="com.bar.providers.InputStreamProvider"/>
      <bean id="longProvider" class="com.bar.providers.LongProvider"/>
    </jaxrs:providers>
  </jaxrs:server>
</beans>

JAXRSServerFactoryBean 类是 Apache CXF 扩展,提供对配置 API 的访问。它有一个 setProvider () 方法,允许您将实例化实体供应商添加到应用程序中。例 51.6 “以编程方式注册实体供应商” 显示用于以编程方式注册实体提供程序的代码。

例 51.6. 以编程方式注册实体供应商

import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
...
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
...
SourceProvider provider = new SourceProvider();
sf.setProvider(provider);
...
Red Hat logoGithubRedditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

© 2024 Red Hat, Inc.