2.7. コンテンツマーシャリングおよびプロバイダー

2.7.1. デフォルトのプロバイダーとデフォルトの Jakarta RESTful Web サービスのコンテンツ

RESTEasy は、いくつかの異なるメッセージ本文を自動的にマーシャリングおよびマーシャリング解除できます。

表2.1 サポートされるメディアタイプと Java タイプ

メディアタイプJava タイプ

application/* +xmltext/* +xml、
application/* +jsonapplication/* +fastinfosetapplication/ atom+*

JAXB アノテーション付きクラス

application/* +xmltext/* +xml

org.w3c.dom.Document

* / *

java.lang.String

* / *

java.io.InputStream

text/plain

プリミティブ、java.lang.String、または String コンストラクターを持つタイプ、または入力用の静的 valueOf(String) メソ背戸、出力用の toOf() メソッド。

* / *

javax.activation.DataSource

* / *

java.io.File

* / *

byte

application/x-www-form-urlencoded

javax.ws.rs.core.MultivaluedMap

2.7.1.1. テキストメディアタイプおよび文字セット

JAX-RS 仕様によると、実装は応答作成時にアプリケーションが提供する文字セットのメタデータに準拠する必要があります。文字セットがアプリケーションによって指定されていない場合、またはサポートされていない文字セットをアプリケーションが指定する場合は、実装で UTF-8 文字セットを使用する必要があります。

反対に、HTTP 仕様によると、送信者が明示的な charset パラメーターを提供しない場合、HTTP 経由で受信すると text タイプのメディアサブタイプは、デフォルトの charset の値である ISO-8859-1 を持つように定義されます。ISO-8859-1 以外の文字セットまたはそのサブセットのデータは、適切な文字セット値でラベル付けする必要があります。

リソースまたはリソースメソッドで指定された文字セットがない場合、RESTEasy は UTF-8 をテキストメディアタイプの文字セットとして使用します。これを行うため、RESTEasy は明示的な charset パラメーターを content-type 応答ヘッダーに追加します。

テキストメディアタイプに UTF-8 が使用されますが、明示的な charset パラメーターが追加されない場合は、コンテキストパラメーター resteasy.add.charsetfalse に設定します。このパラメーターのデフォルト値は true です。

注記

以下は、テキストメディアタイプです。

  • タイプ text と任意のサブタイプを含むメディアタイプ
  • タイプ application を持つメディアタイプ、xml で始まるサブタイプ。これには、application/xml-external-parsed-entity および application/xml-dtd が含まれます。

2.7.2. @Provider クラスを使用したコンテンツマーシャルリング

JAX-RS 仕様では、独自の要求/応答本体のリーダーおよびライターをプラグインできます。そのためには、クラスに @Provider アノテーションを付け、ライターに @Produces を指定し、リーダーに @Consumes タイプを指定します。MessageBodyReader/Writer インターフェイスも実装する必要があります。

@Provider を使用してアノテーションが付けられたクライアントプロバイダーは、JAX-RS コンテナーランタイムのクライアントインスタンスごとに登録してアノテーションを処理する必要があります。不要または重複したクライアントプロバイダー登録の問題を回避するために、システムプロパティー resteasy.client.providers.annotations.disabled は、@Provider アノテーションが付けられたクライアントプロバイダーのデフォルトの処理を無効にます。

RESTEasy ServletContextLoader は自動的に、@Provider アノテーションが付けられたクラスの WEB-INF/lib およびクラスディレクトリーを自動的にスキャンします。または、web.xml ファイルで手動で設定することができます。

2.7.3. Providers ユーティリティークラス

javax.ws.rs.ext.Providers は、MessageBodyReadersWritersContextResolversExceptionMappers を検索できるようにする単純なインジェクト可能なインターフェイスです。これは、他のランダムなコンテンツタイプを埋め込みする multipart プロバイダーおよびコンテンツタイプの実装に非常に便利です。

public interface Providers {
  <T> MessageBodyReader<T> getMessageBodyReader(Class<T> type, Type genericType, Annotation annotations[], MediaType mediaType);
  <T> MessageBodyWriter<T> getMessageBodyWriter(Class<T> type, Type genericType, Annotation annotations[], MediaType mediaType);
  <T extends="" throwable=""> ExceptionMapper<T> getExceptionMapper(Class<T> type);
  <T> ContextResolver<T> getContextResolver(Class<T> contextType, MediaType mediaType);
}

Providers インスタンスは MessageBodyReader または Writers にインジェクトできます。

@Provider
@Consumes("multipart/fixed")
public class MultipartProvider implements MessageBodyReader {

  private @Context Providers providers;
  ...
}

2.7.4. ドキュメントマーシャリングの設定

XML ドキュメントパーサーは XXE (XML eXternal Entity) 攻撃と呼ばれる攻撃の形式に依存し、外部エンティティーを拡張すると安全でないファイルが読み込まれます。たとえば、次のドキュメントでは、/etc/passwd ファイルが読み込まれる可能性があります。

<!--?xml version="1.0"?-->
<!DOCTYPE foo
[<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<search>
 <user>bill</user>
 <file>&xxe;<file>
</search>

デフォルトでは、org.w3c.dom.Document ドキュメントの RESTEasy ビルドインアンマーシャルは外部エンティティーを拡張しません。それらは空の文字列に置き換えられます。これは、外部エンティティーを DTD で定義された値に置き換えるように設定できます。そのためには web.xml ファイルで resteasy.document.expand.entity.references コンテキストパラメーターを true に設定します。

例: resteasy.document.expand.entity.references コンテキストパラメーターの設定

<context-param>
 <param-name>resteasy.document.expand.entity.references</param-name>
 <param-value>true</param-value>
</context-param>

この問題に対処するもう 1 つの方法として、RESTEasy がデフォルトで行う DTD を禁止します。この動作を変更するには、resteasy.document.secure.disableDTDs コンテキストパラメーターを false に設定します。

例: resteasy.document.secure.disableDTDs コンテキストパラメーターの設定

<context-param>
 <param-name>resteasy.document.secure.disableDTDs</param-name>
 <param-value>false</param-value>
</context-param>

ドキュメントは、バッファーが大規模なエンティティーやあまりにも多くの属性によって引き継がれる場合にドス攻撃の対象となります。たとえば、以下のエンティティーを定義すると、&foo6; の拡張が 1,000,000 foos になります。

<!--ENTITY foo 'foo'-->
<!--ENTITY foo1 '&foo;&foo;&foo;&foo;&foo;&foo;&foo;&foo;&foo;&foo;'-->
<!--ENTITY foo2 '&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;'-->
<!--ENTITY foo3 '&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;'-->
<!--ENTITY foo4 '&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;'-->
<!--ENTITY foo5 '&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;'-->
<!--ENTITY foo6 '&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;'-->

デフォルトでは、RESTEasy は展開の数とエンティティーごとの属性数を制限します。正確な動作は、基礎となるパーサーによって異なります。この制限を無効にするには、resteasy.document.secure.processing.feature コンテキストパラメーターを false に設定します。

例: resteasy.document.secure.processing.feature コンテキストパラメーターの設定

<context-param>
 <param-name>resteasy.document.secure.processing.feature</param-name>
 <param-value>false</param-value>
</context-param>

2.7.5. MapProvider の使用

MapProvider を使用することで、JAX-RS リソースでマップを受け入れて返すことができます。

例: マップを許可して返すリソース

@Path("manipulateMap")
@POST
@Consumes("application/x-www-form-urlencoded")
@Produces("application/x-www-form-urlencoded")
public MultivaluedMap<String, String> manipulateMap(MultivaluedMap<String, String> map) {
  //do something
  return map;
}

クライアントを使用して JAX-RS リソースにマップを送受信することもできます。

例: クライアント

MultivaluedMap<String, String> map = new MultivaluedHashMap<String, String>();

//add values to the map...

Response response = client.target(generateURL("/manipulateMap"))
                          .request(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
                          .post(Entity.entity(map, MediaType.APPLICATION_FORM_URLENCODED_TYPE));

String data = response.readEntity(String.class);

//handle data...

2.7.6. 文字列ベースのアノテーションのオブジェクトへの変換

@QueryParam@MatrixParam@HeaderParam@PathParam@FormParam を含む JAX-RS @*Param アノテーションは、raw HTTP リクエストで文字列として表されます。これらのタイプのインジェクトされたパラメーターは、それらのオブジェクトに valueOf(String) 静的メソッドまたは String パラメーターを取るコンストラクターがある場合に、オブジェクトに変換できます。

valueOf() メソッドまたは文字列コンストラクターが存在しないクラスや、HTTP リクエストに適さないクラスがある場合、JAX-RS は javax.ws.rs.ext.ParamConverterProvider および javax.ws.rs.ext.ParamConverter を提供して、メッセージパラメーターの値を対応するカスタム Java タイプに変換します。ParamConverterProvider は、JAX-RS ランタイムにプログラムで登録されているか、@Provider アノテーションを付け、プロバイダースキャンフェーズで JAX-RS ランタイムが自動的に検出されるようにする必要があります。

たとえば、以下の手順では、カスタム pos オブジェクトを作成する方法を示します。@QueryParam@PathParam@MatrixParam@HeaderParam などのメッセージパラメーターから POJO オブジェクトへの変換は、ParamConverter インターフェイスおよび ParamConverterProvider インターフェイスの実装によって実行されます。

  1. カスタム POJO クラスを作成します。

    public class POJO {
      private String name;
    
      public String getName() {
        return name;
      }
    
      public void setName(String name) {
        this.name = name;
      }
    }
  2. カスタム POJO Converter クラスを作成します。

    public class POJOConverter implements ParamConverter<POJO> {
      public POJO fromString(String str) {
        System.out.println("FROM STRNG: " + str);
        POJO pojo = new POJO();
        pojo.setName(str);
        return pojo;
      }
    
      public String toString(POJO value) {
        return value.getName();
      }
    }
  3. カスタム POJO Converter Provider クラスを作成します。

    public class POJOConverterProvider implements ParamConverterProvider {
      @Override
      public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
        if (!POJO.class.equals(rawType)) return null;
        return (ParamConverter<T>)new POJOConverter();
      }
    }
  4. カスタム MyResource クラスを作成します。

    @Path("/")
    public class MyResource {
      @Path("{pojo}")
      @PUT
      public void put(@QueryParam("pojo") POJO q, @PathParam("pojo") POJO pp, @MatrixParam("pojo") POJO mp,
        @HeaderParam("pojo") POJO hp) {
        ...
      }
    }
ParamConverter の機能の拡張

JAX-RS セマンティックでは、ParamConverter は各オブジェクトを表す単一の文字列を変換します。RESTEasy はセマンティックを拡張して、ParamConverter が複数オブジェクトの文字列表現を解析できるようにし、List<T>Set<T>SortedSet<T>array、またはその他の複数値のデータ構造の生成を可能にします。

たとえば、以下のリソースについて見てみましょう。

@Path("queryParam")
public static class TestResource {
   @GET
   @Path("")
   public Response conversion(@QueryParam("q") List<String> list) {
      return Response.ok(stringify(list)).build();
   }
}

private static <T> String stringify(List<T> list) {
   StringBuffer sb = new StringBuffer();
   for (T s : list) {
      sb.append(s).append(',');
   }
   return sb.toString();
}

以下のように、標準の表記を使用して TestResource を呼び出します。

@Test
public void testQueryParamStandard() throws Exception {
   ResteasyClient client = new ResteasyClientBuilder().build();
   Invocation.Builder request = client.target("http://localhost:8081/queryParam?q=20161217&q=20161218&q=20161219").request();
   Response response = request.get();
   System.out.println("response: " + response.readEntity(String.class));
}

結果: response: 20161217,20161218,20161219,.

代わりにコンマ区切りの表記を使用する場合は、以下を追加できます。

public static class MultiValuedParamConverterProvider implements ParamConverterProvider
   @SuppressWarnings("unchecked")
   @Override
   public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
      if (List.class.isAssignableFrom(rawType)) {
         return (ParamConverter<T>) new MultiValuedParamConverter();
      }
      return null;
   }
}

public static class MultiValuedParamConverter implements ParamConverter<List<?>> {
   @Override
   public List<?> fromString(String param) {
      if (param == null || param.trim().isEmpty()) {
         return null;
      }
      return parse(param.split(","));
   }

   @Override
   public String toString(List<?> list) {
      if (list == null || list.isEmpty()) {
         return null;
      }
      return stringify(list);
   }

   private static List<String> parse(String[] params) {
      List<String> list = new ArrayList<String>();
      for (String param : params) {
         list.add(param);
      }
      return list;
   }
}

これで、以下のように TestResource を呼び出すことができます。

@Test
public void testQueryParamCustom() throws Exception {
   ResteasyClient client = new ResteasyClientBuilder().build();
   Invocation.Builder request = client.target("http://localhost:8081/queryParam?q=20161217,20161218,20161219").request();
   Response response = request.get();
   System.out.println("response: " + response.readEntity(String.class));
}

取得: : 20161217,20161218,20161219,

この場合では、MultiValuedParamConverter.fromString() 関数は ArrayList を作成して返すため、TestResource.conversion() 関数を書き換えることができます。

@Path("queryParam")
public static class TestResource {

   @GET
   @Path("")
   public Response conversion(@QueryParam("q") ArrayList<String> list) {
      return Response.ok(stringify(list)).build();
   }
}

または MultiValuedParamConverter は再書き込みして LinkList を返すことができます。また、TestResource.conversion() のパラメーターリストは、List または LinkedList のいずれかになります。

最後に、この拡張はアレイにも機能することに注意してください。以下に例を示します。

public static class Foo {
    private String foo;
    public Foo(String foo) {
        this.foo = foo;
    }
    public String getFoo() {
        return foo;
    }
}

public static class FooArrayParamConverter implements ParamConverter < Foo[] > {
    @Override
    public Foo[] fromString(String value) {
        String[] ss = value.split(",");
        Foo[] fs = new Foo[ss.length];
        int i = 0;
        for (String s: ss) {
            fs[i++] = new Foo(s);
        }
        return fs;
    }

    @Override
    public String toString(Foo[] values) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < values.length; i++) {
            sb.append(values[i].getFoo()).append(",");
        }
        if (sb.length() > 0) {
            sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }
}

@Provider
public static class FooArrayParamConverterProvider implements ParamConverterProvider {
    @SuppressWarnings("unchecked")
    @Override
    public < T > ParamConverter < T > getConverter(Class < T > rawType, Type genericType, Annotation[] annotations) {
        if (rawType.equals(Foo[].class));
        return (ParamConverter < T > ) new FooArrayParamConverter();
    }
}

@Path("")
public static class ParamConverterResource {

    @GET
    @Path("test")
    public Response test(@QueryParam("foos") Foo[] foos) {
        return Response.ok(new FooArrayParamConverter().toString(foos)).build();
    }
}
java.util.Optional パラメータータイプ

RESTEasy は、複数の追加の java.util.Optional パラメータータイプを提供します。これらのパラメータータイプは、ラッパーオブジェクトタイプとして機能します。これにより、ユーザーは任意のタイプパラメーターを入力でき、Optional.orElse() などのメソッドを使用してすべての null チェックを削除できます。

@Path("/double")
@GET
public String optDouble(@QueryParam("value") OptionalDouble value) {
    return Double.toString(value.orElse(4242.0));
}

上記の例は、OptionalDouble をパラメータータイプとして使用できることを示しています。値が @QueryParam で指定されていない場合は、デフォルト値が返されます。以下のパラメータータイプでは、オプションのパラメーターがサポートされています。

  • @QueryParam
  • @MatrixParam
  • @FormParam
  • @HeaderParam
  • @CookieParam

2.7.7. シリアライズ可能なプロバイダー

信用できないソースから Java オブジェクトをデシリアライズすることは危険です。そのため、org.jboss.resteasy.plugins.providers.SerializableProvider はデフォルトで無効になっています。このプロバイダーの使用は推奨されません。

2.7.8. JSON プロバイダー

2.7.8.1. RESTEasy Jackson2 での JsonFilter サポート

JsonFilter は、@JsonFilter でクラスにアノテーションを付けることで、動的なフィルタリングを容易にします。以下の例では、nameFilter クラスからフィルターインスタンスへのマッピングを定義し、インスタンスを JSON 形式にシリアライズする際に Bean プロパティーを除外します。

@JsonFilter(value="nameFilter")
public class Jackson2Product {
    protected String name;
    protected int id;
    public Jackson2Product() {
    }
    public Jackson2Product(final int id, final String name) {
        this.id = id;
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
}

@JsonFilter はリソースクラスにアノテーションを付け、JSON 応答でシリアライズすべきでないプロパティーをフィルターアウトします。フィルター ID とインスタンスをマップするには、別の Jackson クラスを作成し、以下の例のようにその ID とフィルターインスタンスマップを追加する必要があります。

public class ObjectFilterModifier extends ObjectWriterModifier {
	public ObjectFilterModifier() {
	}
	@Override
	public ObjectWriter modify(EndpointConfigBase<?> endpoint,
		MultivaluedMap<String, Object> httpHeaders, Object valueToWrite,
		ObjectWriter w, JsonGenerator jg) throws IOException {

		FilterProvider filterProvider = new SimpleFilterProvider().addFilter(
			"nameFilter",
			SimpleBeanPropertyFilter.filterOutAllExcept("name"));
		return w.with(filterProvider);
	}
}

上記の例では、メソッド modify() は応答を記述する前に、name プロパティー以外のすべてのプロパティーをフィルタリングします。これを機能させるには、RESTEasy はこのマッピング情報を知っておく必要があります。以下の例が示すように、WriterInterceptor またはサーブレットフィルターのいずれかに設定できます。

例: WriterInterceptor を使用した ObjectFilterModifier の設定

@Provider
public class JsonFilterWriteInterceptor implements WriterInterceptor{

	private ObjectFilterModifier modifier = new ObjectFilterModifier();
	@Override
	public void aroundWriteTo(WriterInterceptorContext context)
		throws IOException, WebApplicationException {
	    	//set a threadlocal modifier
            ObjectWriterInjector.set(modifier);
                context.proceed();
	}

}

例: サーブレットフィルターを使用した ObjectFilterModifier の設定

public class ObjectWriterModifierFilter implements Filter {
	private static ObjectFilterModifier modifier = new ObjectFilterModifier();

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
		FilterChain chain) throws IOException, ServletException {
		    ObjectWriterInjector.set(modifier);
		    chain.doFilter(request, response);
	}

	@Override
	public void destroy() {
	}

}

これで、RESTEasy は ObjectFilterModifierThreadLocal 変数から取得して、応答の作成前に ObjectWriter を変更するように設定できます。

2.7.8.2. JSON Binding

RESTEasy は JSON Binding と JSON-P の両方に対応しています。仕様によると、JsonValue とそのサブタイプを除き、すべてのタイプのエンティティーで、JSON Binding のエンティティープロバイダーの優先度は JSON-P のエンティティープロバイダーよりも高くなります。

resteasy-json-binding-provider モジュールからの JsonBindingProvider は、JSON-B のサポートを提供します。JAX-RS 2.1 の要件を満たすため、JsonBindingProvider プロバイダーの優先度は、Jackson ペイロードなどの JSON ペイロードを処理する他のプロバイダーよりも高くなります。JSON Binding の Jakarta EE と同等の Jakarta EE は、Jakarta JSON Binding Specification 1.0 仕様 に含まれています。

同じ入力では、Jackson および Jakarta JSON Binding リファレンス実装からの JSON 出力は異なる場合があります。後方互換性を維持するには、resteasy.preferJacksonOverJsonB コンテキストプロパティーを true に設定し、現在のデプロイメントの JsonBindingProvider 設定を無効にします。

JBoss EAP は、同じ名前のシステムプロパティーを設定して、resteasy.preferJacksonOverJsonB コンテキストプロパティーのデフォルト値を指定することをサポートします。コンテキストおよびシステムプロパティーに値が設定されていない場合は、Jackson アノテーションの JAX-RS または Jakarta RESTful Web Services デプロイメントをスキャンし、これらのアノテーションのいずれかが見つかった場合にプロパティーを true に設定します。

2.7.9. アクセッティングプロバイダー

2.7.9.1. アイザーおよび XML プロバイダー

RESTEasy は、XML のメソッドプロバイダーサポートを提供します。

@XmlHeader および @Stylesheet

RESTEasy は、@org.jboss.resteasy.annotations.providers.jaxb.XmlHeader アノテーションを使用した XML ヘッダーの設定を提供します。

例: @XmlHeader アノテーションの使用

@XmlRootElement
public static class Thing {
   private String name;

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }
}

@Path("/test")
public static class TestService {

   @GET
   @Path("/header")
   @Produces("application/xml")
   @XmlHeader("<?xml-stylesheet type='text/xsl' href='${baseuri}foo.xsl' ?>")
   public Thing get() {
      Thing thing = new Thing();
      thing.setName("bill");
      return thing;
   }
}

@XmlHeader は、XML 出力に XML スタイルシートヘッダーがあることを確認します。

RESTEasy にはスタイルシートヘッダーの便利なアノテーションがあります。

例: @Stylesheet アノテーションの使用

@XmlRootElement
public static class Thing {
   private String name;

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }
}

@Path("/test")
public static class TestService {

   @GET
   @Path("/stylesheet")
   @Produces("application/xml")
   @Stylesheet(type="text/css", href="${basepath}foo.xsl")
   @Junk
   public Thing getStyle() {
      Thing thing = new Thing();
      thing.setName("bill");
      return thing;
   }
}

2.7.9.2. JAXB および JSON プロバイダー

RESTEasy を使用すると、JSON プロバイダーを使用して JSON との間でアノテーションが付けられた pos をマーシャリングできます。このプロバイダーは、このタスクを実行するために Jackson JSON ライブラリーをラップします。Java Beans ベースのモデルと、JAXB に類似した API があります。

Jackson にすでに Jakarta RESTful Web Services 統合が含まれていますが、RESTEasy によって拡張されました。これをプロジェクトに組み込むには、Maven 依存関係を更新する必要があります。

Jackson の Maven 依存関係

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jackson2-provider</artifactId>
    <version>${version.org.jboss.resteasy}</version>
    <scope>provided</scope>
</dependency>

注記

RESTEasy のデフォルトの JSON プロバイダーは Jackson2 です。JBoss EAP の以前のバージョンには Jackson1 JSON プロバイダーが含まれていました。Jackson1 プロバイダーから既存のアプリケーションを移行する方法の詳細は、JBoss EAP Migration Guide を参照してください。Jackson1 プロバイダーを引き続き使用する場合は 、Maven の依存関係を明示的に更新して取得する必要があります

注記

以前のバージョンの JBoss EAP の RESTEasy のデフォルト JSON プロバイダーは Jettison でしたが、JBoss EAP 7 では非推奨になっています。詳細は、JBoss EAPMigration Guide を参照してください。

JSON プロバイダーの例

@XmlRootElement
public static class Thing {
  private String name;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

@Path("/test")
public static class TestService {
  @GET
  @Path("/thing")
  @Produces("application/json")
  public Thing get() {
    Thing thing = new Thing();
    thing.setName("the thing");
    return thing;
  }
}

2.7.9.2.1. Java 8 の Jackson モジュールサポート

ここでは、Maven の依存関係を説明します。また、コアの Jackson モジュールが Java 8 ランタイム環境を必要としない場合に Java 8 機能をサポートするために必要な Jackson モジュールを登録する方法について説明します。これらの Jackson モジュールには以下が含まれます。

  • Java 8 データ型
  • Java 8 日付/時刻

以下の Maven 依存関係を追加します。

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

以下の例のように、findAndRegisterModules() または ObjectMapper.registerModule() を使用してすべてのモジュールを検索し、登録することができます。

ObjectMapper mapper = new ObjectMapper();
mapper.findAndRegisterModules();
ObjectMapper mapper = new ObjectMapper()
   .registerModule(new ParameterNamesModule())
   .registerModule(new Jdk8Module())
   .registerModule(new JavaTimeModule());

例: 期間のデータタイプ

@GET
@Path("/duration")
@Produces(MediaType.APPLICATION_JSON)
public Duration getDuration() {
    return Duration.ofSeconds(5, 6);
}

例: 任意のデータタイプ

@GET
@Path("/optional/{nullParam}")
@Produces(MediaType.APPLICATION_JSON)
public Optional<String> getOptional(@PathParam("nullParam") boolean nullParameter) {
    return nullParameter ? Optional.<String>empty() : Optional.of("info@example.com");
}

RESTEasy でこれらの Jackson モジュールを使用するには、ContextResolver のカスタム実装を使用する必要があります。

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JacksonDatatypeJacksonProducer implements ContextResolver<ObjectMapper> {
    private final ObjectMapper json;
    public JacksonDatatypeJacksonProducer() throws Exception {
        this.json = new ObjectMapper()
                .findAndRegisterModules()
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }
    @Override
    public ObjectMapper getContext(Class<?> objectType) {
        return json;
    }
}
2.7.9.2.2. デフォルトの Jackson プロバイダーの切り替え

JBoss EAP 7 には Jackson 2.6.x 以上が含まれ、resteasy-jackson2-provider がデフォルトの Jackson プロバイダーになりました。

以前のリリースの JBoss EAP に含まれていたデフォルトの resteasy-jackson-provider に切り替えるには、新しいプロバイダーを除外し、jboss-deployment-structure.xml アプリケーションデプロイメント記述子ファイルに以前のプロバイダーの依存関係を追加します。

<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
   <deployment>
       <exclusions>
           <module name="org.jboss.resteasy.resteasy-jackson2-provider"/>
       </exclusions>
       <dependencies>
           <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
       </dependencies>
   </deployment>
</jboss-deployment-structure>

2.7.10. デコレーターの作成

RESTEasy の資料プロバイダーには、Marshaller および Un Marketplaceler インスタンスを切り離すプラグ可能な方法があります。メソッドを切り離すために使用できる Marshaller または Un markler インスタンスのいずれかをトリガーできるアノテーションを作成できます。

RESTEasy を使用したデコレーターの作成
  1. Processor クラスを作成します。

    1. DecoratorProcessor<Target, Annotation> を実装するクラスを作成します。ターゲットは JAXB Marshaller または Unmarshaller クラスのいずれかになります。アノテーションは、ステップ 2 で作成されます。
    2. クラスに @DecorateTypes アノテーションを付け、デコレーターがデコレートする必要のある MIME タイプを宣言します。
    3. decorate 関数内にプロパティーまたは値を設定します。

      例: Processor クラス

      import org.jboss.resteasy.core.interception.DecoratorProcessor;
      import org.jboss.resteasy.annotations.DecorateTypes;
      import javax.xml.bind.Marshaller;
      import javax.xml.bind.PropertyException;
      import javax.ws.rs.core.MediaType;
      import javax.ws.rs.Produces;
      import java.lang.annotation.Annotation;
      
      @DecorateTypes({"text/*+xml", "application/*+xml"})
      public class PrettyProcessor implements DecoratorProcessor<Marshaller, Pretty> {
          public Marshaller decorate(Marshaller target, Pretty annotation,
            Class type, Annotation[] annotations, MediaType mediaType) {
          target.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
          }
      }

  2. アノテーションを作成します。

    1. @Decorator アノテーションが付けられたカスタムインターフェイスを作成します。
    2. @Decorator アノテーションのプロセッサーおよびターゲットを宣言します。プロセッサーは、ステップ 1 で作成されます。ターゲットは JAXB Marshaller または Unmarshaller クラスのいずれかになります。

      例: @Decorator アノテーションのあるカスタムインターフェイス

      import org.jboss.resteasy.annotations.Decorator;
      
      @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
      @Retention(RetentionPolicy.RUNTIME)
      @Decorator(processor = PrettyProcessor.class, target = Marshaller.class)
      public @interface Pretty {}

  3. 手順 2 で作成したアノテーションを関数に追加して、入力または出力のいずれかがマーシャリングされるときにデコレートされるようにします。

Jakarta RESTful Web Services Web サービス内に適用できる JAXB デコレーターを作成できるようになりました。

2.7.11. JAX-RS の multipart プロバイダー

multipart MIME 形式は、単一のメッセージに埋め込まれたコンテンツボディーのリストを渡すために使用されます。multipart MIME 形式の一例として、multipart/form-data MIME タイプが挙げられます。これは Web アプリケーションの HTML 形式のドキュメントにあり、通常はファイルをアップロードするために使用されます。この MIME タイプの form-data 形式は、インラインコンテンツのそれぞれに関連する名前がある以外は、他の multipart 形式と同じです。

RESTEasy は、multipart/form-data および multipart/* MIME タイプを許可します。RESTEasy は、multipart タイプの読み書きや、任意の List (任意の multipart タイプ) および Map (multipart/form-data のみ) オブジェクトのマーシャリングを行うためのカスタム API も提供します。

重要

Seam の org.jboss.seam.web.MultipartFilter や Spring の org.springframework.web.multipart.MultipartResolver など、フィルターやインターセプターの支援により、multipart 解析を自動的に行うフレームワークが多数あります。ただし、着信 multipart 要求ストリームは 1 回のみ解析できます。multipart を使用する RESTEasy ユーザーは、RESTEasy がそれを取得する前にストリームを解析しないようにする必要があります。

2.7.11.1. multipart データでの入力

JAX-RS サービスを記述する際に、RESTEasy は org.jboss.resteasy.plugins.providers.multipart.MultipartInput MultipartInput インターフェイスを提供し、任意の multipart MIME タイプで読み取ることができます。

package org.jboss.resteasy.plugins.providers.multipart;

public interface MultipartInput {

   List<InputPart> getParts();
   String getPreamble();

   // You must call close to delete any temporary files created
   // Otherwise they will be deleted on garbage collection or on JVM exit
   void close();
}

public interface InputPart {

   MultivaluedMap<String, String> getHeaders();
   String getBodyAsString();
   <T> T getBody(Class<T> type, Type genericType) throws IOException;
   <T> T getBody(org.jboss.resteasy.util.GenericType<T> type) throws IOException;
   MediaType getMediaType();
   boolean isContentTypeFromMessage();
}

MultipartInput は、multipart メッセージの各パーツにアクセスできる簡単なインターフェイスです。各パーツは InputPart インターフェイスによって表示され、各パートにはそれに関連するヘッダーのセットがあります。getBody() メソッドのいずれかを呼び出すと、この部分のマーシャルを解除できます。genericType パラメーターは null にすることができますが、type パラメーターは設定する必要があります。RESTEasy は、パーツのメディアタイプおよび渡すタイプ情報に基づいて MessageBodyReader を検出します。

2.7.11.1.1. multipart/mixed での入力

例: パーツのマーシャリング解除

@Path("/multipart")
public class MyService {

    @PUT
    @Consumes("multipart/mixed")
    public void put(MultipartInput input) {
        List<Customer> customers = new ArrayList...;
        for (InputPart part : input.getParts()) {
            Customer cust = part.getBody(Customer.class, null);
            customers.add(cust);
        }
        input.close();
    }
}

注記

上記の例では、Customer クラスに Mission のアノテーションが付けられていることを前提としています。

汎用型のメタデータの影響を受けるボディー部分をマーシャリング解除する必要があることも考えられます。この場合は、org.jboss.resteasy.util.GenericType クラスを使用できます。

例: 汎用型メタデータに影響を受けやすいタイプのマーシャリング解除

@Path("/multipart")
public class MyService {

    @PUT
    @Consumes("multipart/mixed")
    public void put(MultipartInput input) {
        for (InputPart part : input.getParts()) {
            List<Customer> cust = part.getBody(new GenericType<List<Customer>>() {});
        }
        input.close();
    }
}

GenericType の使用は、ランタイム時に汎用型情報を取得する唯一の方法であるため、必要になります。

2.7.11.1.2. multipart/mixed および java.util.List による入力

ボディーの部分が均一である場合は、各部分と各パーツを手動でマーシャリング解除する必要はありません。入力パラメーターとして java.util.List を指定できます。List タイプ宣言の汎用パラメーターを使用してマーシャリング解除されている型が必要です。

例: CustomersList のマーシャリング解除

@Path("/multipart")
public class MyService {

    @PUT
    @Consumes("multipart/mixed")
    public void put(List<Customer> customers) {
        ...
    }
}

注記

上記の例では、Customer クラスに Mission のアノテーションが付けられていることを前提としています。

2.7.11.1.3. multipart/form-data による入力

JAX-RS サービスを記述する際に、RESTEasy はマ multipart/form-data MIME タイプでの読み込みを可能にするインターフェイスを提供します。multipart/form-data は、Web アプリケーションの HTML 形式のドキュメントにあり、通常はファイルをアップロードするために使用されます。form-data 形式は、インラインコンテンツのそれぞれに関連する名前がある以外は、他の multipart 形式と同じです。from-data 入力に使用されるインターフェイスは org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput です。

例: MultipartFormDataInput インターフェイス

public interface MultipartFormDataInput extends MultipartInput {

    @Deprecated
    Map<String, InputPart> getFormData();
    Map<String, List<InputPart>> getFormDataMap();
    <T> T getFormDataPart(String key, Class<T> rawType, Type genericType) throws IOException;
    <T> T getFormDataPart(String key, GenericType<T> type) throws IOException;
}

これは、前述の MultipartInput と同じように機能します。

2.7.11.1.4. ultipart/form-data を含む java.utol.Map

form-data では、ボディーの部分が均一である場合は、あらゆるパーツを手動でマーシャリング解除する必要はありません。入力パラメーターとして java.util.Map を指定できます。List タイプ宣言の汎用パラメーターを使用してマーシャリング解除されている型が必要です。

例: Customer オブジェクトの Map のマーシャリング解除

@Path("/multipart")
public class MyService {

    @PUT
    @Consumes("multipart/form-data")
    public void put(Map<String, Customer> customers) {
        ...
    }
}

注記

上記の例では、Customer クラスに Mission のアノテーションが付けられていることを前提としています。

2.7.11.1.5. multipart/related での入力

JAX-RS サービスを記述する際に、RESTEasy はマ multipart/related MIME タイプでの読み込みを可能にするインターフェイスを提供します。multipart/related 関連は、メッセージパーツを個別に考慮すべきではなく、アグリゲート全体の一部としてではなく、RFC 2387 で定義されることを示します。

multipart/related の使用例として、単一メッセージでイメージを含む Web ページを送信する方法があります。multipart/related メッセージにはすべて、メッセージの他の部分を参照する根源/開始部分があります。パーツは、Content-ID ヘッダーで識別されます。関連する入力に使用されるインターフェイスは org.jboss.resteasy.plugins.providers.multipart.MultipartRelatedInput です。

例: MultipartRelatedInput インターフェイス

public interface MultipartRelatedInput extends MultipartInput {

    String getType();
    String getStart();
    String getStartInfo();
    InputPart getRootPart();
    Map<String, InputPart> getRelatedMap();
}

これは MultipartInput と同じように機能します。

2.7.11.2. multipart データでの出力

RESTEasy は、multipart データを出力するための簡単な API を提供します。

package org.jboss.resteasy.plugins.providers.multipart;

public class MultipartOutput {

    public OutputPart addPart(Object entity, MediaType mediaType)
    public OutputPart addPart(Object entity, GenericType type, MediaType mediaType)
    public OutputPart addPart(Object entity, Class type, Type genericType, MediaType mediaType)
    public List<OutputPart> getParts()
    public String getBoundary()
    public void setBoundary(String boundary)
}

public class OutputPart {

    public MultivaluedMap<String, Object> getHeaders()
    public Object getEntity()
    public Class getType()
    public Type getGenericType()
    public MediaType getMediaType()
}

multipart データを出力するには、MultipartOutput オブジェクトを作成し、addPart() メソッドを呼び出します。RESTEasy は、エンティティーオブジェクトをマーシャリングするための MessageBodyWriter を自動的に検出します。MultipartInput と同様に、汎用型メタデータに影響しやすいマーシャリングがある場合があります。この場合は、GenericType を使用します。通常は、オブジェクトとその MediaType を渡すだけで十分です。

例: multipart/mixed 形式の戻り値

@Path("/multipart")
public class MyService {

    @GET
    @Produces("multipart/mixed")
    public MultipartOutput get() {

        MultipartOutput output = new MultipartOutput();
        output.addPart(new Customer("bill"), MediaType.APPLICATION_XML_TYPE);
        output.addPart(new Customer("monica"), MediaType.APPLICATION_XML_TYPE);
        return output;
    }
}

注記

上記の例では、Customer クラスに Jakarta XML Binding のアノテーションが付けられていることを前提としています。

2.7.11.2.1. java.util.List でのマルチ出力

ボディーの部分が均一である場合は、各パーツおよび各パーツを手動でマーシャリングしたり、MultipartOutput オブジェクトを使用したりする必要はありません。java.util.List を指定できます。これは、List 型宣言の汎用パラメーターでマーシャリングされる汎用型である必要があります。また、各パーツのメディアタイプを指定するには @PartType アノテーションを使用してメソッドにアノテーションを付ける必要もあります。

例: Customer オブジェクトの List 戻り値

@Path("/multipart")
public class MyService {

    @GET
    @Produces("multipart/mixed")
    @PartType("application/xml")
    public List<Customer> get(){
        ...
    }
}

注記

上記の例では、Customer クラスに Jakarta XML Binding のアノテーションが付けられていることを前提としています。

2.7.11.2.2. multipart/form-data による出力

RESTEasy は、multipart/form-data を出力する簡単な API を提供します。

package org.jboss.resteasy.plugins.providers.multipart;

public class MultipartFormDataOutput extends MultipartOutput {

    public OutputPart addFormData(String key, Object entity, MediaType mediaType)
    public OutputPart addFormData(String key, Object entity, GenericType type, MediaType mediaType)
    public OutputPart addFormData(String key, Object entity, Class type, Type genericType, MediaType mediaType)
    public Map<String, OutputPart> getFormData()
}

multipart/form-data を出力するには、MultipartFormDataOutput オブジェクトを作成し、addFormData() メソッドを呼び出します。RESTEasy は、エンティティーオブジェクトをマーシャリングするための MessageBodyWriter を自動的に検出します。MultipartInput と同様に、汎用型メタデータに影響しやすいマーシャリングがある場合があります。この場合は、GenericType を使用します。通常は、オブジェクトとその MediaType を渡すだけで十分です。

例: multipart/form-data 形式の戻り値

@Path("/form")
public class MyService {

    @GET
    @Produces("multipart/form-data")
    public MultipartFormDataOutput get() {

        MultipartFormDataOutput output = new MultipartFormDataOutput();
        output.addPart("bill", new Customer("bill"), MediaType.APPLICATION_XML_TYPE);
        output.addPart("monica", new Customer("monica"), MediaType.APPLICATION_XML_TYPE);
        return output;
    }
}

注記

上記の例では、Customer クラスに Jakarta XML Binding のアノテーションが付けられていることを前提としています。

2.7.11.2.3. java.util.Map による Multipart FormData Output

ボディーの部分が均一である場合は、各パーツを手動でマーシャリングしたり、MultipartFormDataOutput オブジェクトを使用したりする必要はありません。java.util.Map のみを指定できます。この Map には、Map タイプ宣言の汎用パラメーターを使用してマーシャリングする汎用型が必要です。また、各パーツのメディアタイプを指定するには @PartType アノテーションを使用してメソッドにアノテーションを付ける必要もあります。

例: Customer オブジェクトの Map 戻り値

@Path("/multipart")
public class MyService {

    @GET
    @Produces("multipart/form-data")
    @PartType("application/xml")
    public Map<String, Customer> get() {
        ...
    }
}

注記

上記の例では、Customer クラスに Mission のアノテーションが付けられていることを前提としています。

2.7.11.2.4. multipart/related での入力

RESTEasy は、multipart/related を出力するためのシンプルな API を提供します。

package org.jboss.resteasy.plugins.providers.multipart;

public class MultipartRelatedOutput extends MultipartOutput {

    public OutputPart getRootPart()
    public OutputPart addPart(Object entity, MediaType mediaType,
        String contentId, String contentTransferEncoding)
    public String getStartInfo()
    public void setStartInfo(String startInfo)
}

multipart/related を出力するには、MultipartRelatedOutput オブジェクトを作成し、addPart() メソッドを呼び出します。最初に追加された部分は、multipart/related ジのルート部分として使用され、RESTEasy はエンティティーオブジェクトをマーシャリングするための MessageBodyWriter を自動的に検出します。MultipartInput と同様に、汎用型メタデータに影響しやすいマーシャリングがある場合があります。この場合は、GenericType を使用します。通常は、オブジェクトとその MediaType を渡すだけで十分です。

例: 2 つのイメージを送信する multipart/related フォーマットの返信

@Path("/related")
public class MyService {

    @GET
    @Produces("multipart/related")
    public MultipartRelatedOutput get() {

        MultipartRelatedOutput output = new MultipartRelatedOutput();
        output.setStartInfo("text/html");

        Map<String, String> mediaTypeParameters = new LinkedHashMap<String, String>();
        mediaTypeParameters.put("charset", "UTF-8");
        mediaTypeParameters.put("type", "text/html");
        output.addPart(
            "<html><body>\n"
            + "This is me: <img src='cid:http://example.org/me.png' />\n"
            + "<br />This is you: <img src='cid:http://example.org/you.png' />\n"
            + "</body></html>",
            new MediaType("text", "html", mediaTypeParameters),
            "<mymessage.xml@example.org>", "8bit");
        output.addPart("// binary octets for me png",
            new MediaType("image", "png"), "<http://example.org/me.png>",
            "binary");
        output.addPart("// binary octets for you png", new MediaType(
            "image", "png"),
            "<http://example.org/you.png>", "binary");
        client.putRelated(output);
        return output;
    }
}

注記

上記の例では、Customer クラスに Jakarta XML Binding のアノテーションが付けられていることを前提としています。

2.7.11.3. Multipart フォームの POJO へのマッピング

multipart/form-data パッケージについて正確な知識がある場合は、POJO クラスとマップすることができます。これは、org.jboss.resteasy.annotations.providers.multipart.MultipartForm アノテーション (@MultipartForm) および JAX-RS @FormParam アノテーションを使用して実行できます。これを行うには、少なくともデフォルトのコンストラクターを使用して POJO を定義し、@FormParams でフィールドとプロパティーにアノテーションを付ける必要があります。これらの @FormParams にも org.jboss.resteasy.annotations.providers.multipart.PartType (@PartType) のアノテーションを付ける必要があります (出力を作成する場合)。

例: multipart フォームの POJO へのマッピング

public class CustomerProblemForm {

    @FormParam("customer")
    @PartType("application/xml")
    private Customer customer;

    @FormParam("problem")
    @PartType("text/plain")
    private String problem;

    public Customer getCustomer() { return customer; }
    public void setCustomer(Customer cust) { this.customer = cust; }
    public String getProblem() { return problem; }
    public void setProblem(String problem) { this.problem = problem; }
}

POJOI クラスを定義したら、それを使用して multipart/form-data を表すことができます。

例: CustomerProblemForm の送信

@Path("portal")
public interface CustomerPortal {

    @Path("issues/{id}")
    @Consumes("multipart/form-data")
    @PUT
    public void putProblem(@MultipartForm CustomerProblemForm,
                           @PathParam("id") int id);
}

// Somewhere using it:
{
    CustomerPortal portal = ProxyFactory.create(CustomerPortal.class, "http://example.com");
    CustomerProblemForm form = new CustomerProblemForm();
    form.setCustomer(...);
    form.setProblem(...);

    portal.putProblem(form, 333);
}

@MultipartForm アノテーションは、オブジェクトに @FormParam があり、そこからマーシャリングが必要であることを RESTEasy に指示します。また、同じオブジェクトを使用して multipart データを受信することもできます。

例: CustomerProblemForm の受信

@Path("portal")
public class CustomerPortalServer {

    @Path("issues/{id})
    @Consumes("multipart/form-data")
    @PUT
    public void putIssue(@MultipartForm CustomerProblemForm,
                         @PathParam("id") int id) {
       ... write to database...
    }
}

2.7.11.4. XML-binary Optimized Packaging (XOP)

一部のバイナリーコンテンツも保持する JAXB アノテーションが付けられた POJO がある場合は、バイナリーを base64 や hex などのどの方法でもエンコードする必要がない方法で送信することを選択できます。これは XOP を使用して実現でき、便利な POJO を使用しながらトランスポートが高速になります。

RESTEasy は、multipart/related としてパッケージ化された XOP メッセージを許可します。

XOP を設定するには、まずに JAXB アノテーションが付けられたアノテーションが付いた属性が必要です。

例: Jakarta XML Binding POJO

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public static class Xop {

    private Customer bill;
    private Customer monica;

    @XmlMimeType(MediaType.APPLICATION_OCTET_STREAM)
    private byte[] myBinary;

    @XmlMimeType(MediaType.APPLICATION_OCTET_STREAM)
    private DataHandler myDataHandler;

    // methods, other fields ...
}

注記

@XmlMimeType はバイナリーコンテンツの mime タイプを指示します。これは XOP パッケージ化を行う必要はありませんが、正確なタイプが分かっている場合は設定することが推奨されます。

上記の myBinary および myDataHandler はバイナリー添付として処理され、XOP オブジェクト全体が XML として送信されます。バイナリーの代わり、それらの参照のみが生成されます。javax.activation.DataHandler は、最も一般的なサポートされているタイプです。java.io.InputStream または javax.activation.DataSource が必要な場合は、DataHandler を使用する必要があります。java.awt.Image および javax.xml.transform.SourceSome も利用できます。

例: XOP でバイナリーコンテンツを送信するクライアント

// our client interface:
@Path("mime")
public static interface MultipartClient {
    @Path("xop")
    @PUT
    @Consumes(MultipartConstants.MULTIPART_RELATED)
    public void putXop(@XopWithMultipartRelated Xop bean);
}

// Somewhere using it:
{
    MultipartClient client = ProxyFactory.create(MultipartClient.class,
        "http://www.example.org");
    Xop xop = new Xop(new Customer("bill"), new Customer("monica"),
        "Hello Xop World!".getBytes("UTF-8"),
        new DataHandler(new ByteArrayDataSource("Hello Xop World!".getBytes("UTF-8"),
        MediaType.APPLICATION_OCTET_STREAM)));
    client.putXop(xop);
}

注記

上記の例では、Customer クラスに JAXB のアノテーションが付けられていることを前提としています。

@Consumes(MultipartConstants.MULTIPART_RELATED) は、multipart/related パッケージを送信するように RESTEasy に指示するために使用されます。@XopWithMultipartRelated は、XOP メッセージ を作成するように RESTEasy に指示するために使用されます。

例: XOP を受信する RESTEasy サーバー

@Path("/mime")
public class XopService {
    @PUT
    @Path("xop")
    @Consumes(MultipartConstants.MULTIPART_RELATED)
    public void putXopWithMultipartRelated(@XopWithMultipartRelated Xop xop) {
        // do very important things here
    }
}

@Consumes(MultipartConstants.MULTIPART_RELATED) は、multipart/related パッケージを読み取るように RESTEasy に指示するために使用されます。@XopWithMultipartRelated は、XOP メッセージを読み取ることを RESTEasy に伝えるために使用されます。RESTEasy サーバーは、@Produces アノテーションを追加し、適切な型を返すことで、同様の方法で XOP 値を生成するように設定できます。

2.7.11.5. multipart メッセージのデフォルトのフォールバックコンテンツタイプの上書き

デフォルトでは、Content-Type ヘッダーが一部にない場合、text/plain; charset=us-ascii がフォールバックとして使用されます。これは MIME RFC によって定義されます。ただし、多くのブラウザーなどの一部の Web クライアントはファイルパーツの Content-Type ヘッダーを送信しますが、multipart/form-data のすべてのフィールドについては送信しません。これにより、サーバー側で文字エンコーディングおよびマーシャリング解除エラーが発生する可能性があります。この問題を修正するには、RESTEasy の PreProcessInterceptor インフラストラクチャーを使用できます。これを使用して、リクエストごとに、RFC に準拠していない別のフォールバック値を動的に定義することができます。

例: * / *; charset=UTF-8 をデフォルトフォールバックとして設定

import org.jboss.resteasy.plugins.providers.multipart.InputPart;

@Provider
@ServerInterceptor
public class ContentTypeSetterPreProcessorInterceptor implements PreProcessInterceptor {

    public ServerResponse preProcess(HttpRequest request, ResourceMethod method)
            throws Failure, WebApplicationException {
        request.setAttribute(InputPart.DEFAULT_CONTENT_TYPE_PROPERTY, "*/*; charset=UTF-8");
        return null;
    }
}

2.7.11.6. 複数パートメッセージのコンテンツタイプの上書き

インターセプターおよび InputPart.DEFAULT_CONTENT_TYPE_PROPERTY 属性を使用すると、デフォルトの Content-Type を設定できます。org.jboss.resteasy.plugins.providers.multipart.InputPart.setMediaType() を呼び出して、任意の入力部分で Content-Type をオーバーライドすることもできます。

例: Content-Type の上書き

@POST
@Path("query")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
public Response setMediaType(MultipartInput input) throws IOException {

    List<InputPart> parts = input.getParts();
    InputPart part = parts.get(0);
    part.setMediaType(MediaType.valueOf("application/foo+xml"));
    String s = part.getBody(String.class, null);
    ...
}

2.7.11.7. multipart メッセージのデフォルトのフォールバック charset の上書き

multipart メッセージの一部が、charset パラメーターのない Content-Type ヘッダーを持つ場合があります。InputPart.DEFAULT_CONTENT_TYPE_PROPERTY プロパティーが設定され、値に charset パラメーターがある場合、その値は charset パラメーターのない既存の Content-Type ヘッダーに追加されます。

また、デフォルトの charset は、定数 InputPart.DEFAULT_CHARSET_PROPERTY (resteasy.provider.multipart.inputpart.defaultCharset) を使用して指定することもできます。

例: デフォルトの charset の指定

import org.jboss.resteasy.plugins.providers.multipart.InputPart;

@Provider
@ServerInterceptor
public class ContentTypeSetterPreProcessorInterceptor implements PreProcessInterceptor {

    public ServerResponse preProcess(HttpRequest request, ResourceMethod method)
            throws Failure, WebApplicationException {
        request.setAttribute(InputPart.DEFAULT_CHARSET_PROPERTY, "UTF-8");
        return null;
    }
}

注記

InputPart.DEFAULT_CONTENT_TYPE_PROPERTYInputPart.DEFAULT_CHARSET_PROPERTY の両方が設定されている場合は、InputLookup .DEFAULT_CHARSET_PROPERTY の値は InputPart.DEFAULT_CONTENT_TYPE_PROPERTY の値の charset を上書きします。

2.7.11.8. RESTEasy クライアントでの multipart エンティティーの送信

multipart プロバイダーを設定する他に、multipart データを送信するように RESTEasy クライアントを設定することもできます。

RESTEasy クライアントクラスの使用

アプリケーションで RESTEasy クライアントクラスを使用するには、Maven 依存関係をプロジェクトの POM ファイルに追加する必要があります。

例: Maven 依存関係

<dependency>
  <groupId>org.jboss.resteasy</groupId>
  <artifactId>resteasy-client</artifactId>
  <version>${version.org.jboss.resteasy}</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.jboss.resteasy</groupId>
  <artifactId>resteasy-multipart-provider</artifactId>
  <version>${version.org.jboss.resteasy}</version>
  <scope>provided</scope>
</dependency>

RESTEasy クライアントを使用した multipart データの送信

multipart データを送信するには、まず RESTEasy Client を設定し、org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput オブジェクトを構築して、multipart データを含めるようにします。次に、クライアントを使用してその MultipartFormDataOutput オブジェクトを javax.ws.rs.core.GenericEntity として送信することができます。

例: RESTEasy クライアント

ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target("http://foo.com/resource");

MultipartFormDataOutput formOutputData = new MultipartFormDataOutput();
formOutputData.addFormData("part1", "this is part 1", MediaType.TEXT_PLAIN);
formOutputData.addFormData("part2", "this is part 2", MediaType.TEXT_PLAIN);

GenericEntity<MultipartFormDataOutput> data = new GenericEntity<MultipartFormDataOutput>(formOutputData) { };

Response response = target.request().put(Entity.entity(data, MediaType.MULTIPART_FORM_DATA_TYPE));

response.close();

2.7.12. RESTEasy Atom サポート

RESTEasy Atom API およびプロバイダーは、RESTEasy が Atom を表すように定義する簡単なオブジェクトモデルです。API の主なクラスは org.jboss.resteasy.plugins.providers.atom パッケージにあります。RESTEasy は JAXB を使用して API のマーシャリングおよびマーシャリング解除に使用します。プロバイダーは JAXB ベースであり、XML を使用した Atom オブジェクトの送信に限定されません。RESTEasy が持つすべての JAXB プロバイダーは、JSON を含む Atom API およびプロバイダーによって再利用できます。

import org.jboss.resteasy.plugins.providers.atom.Content;
import org.jboss.resteasy.plugins.providers.atom.Entry;
import org.jboss.resteasy.plugins.providers.atom.Feed;
import org.jboss.resteasy.plugins.providers.atom.Link;
import org.jboss.resteasy.plugins.providers.atom.Person;

@Path("atom")
public class MyAtomService {

   @GET
   @Path("feed")
   @Produces("application/atom+xml")
   public Feed getFeed() throws URISyntaxException {
      Feed feed = new Feed();
      feed.setId(new URI("http://example.com/42"));
      feed.setTitle("My Feed");
      feed.setUpdated(new Date());
      Link link = new Link();
      link.setHref(new URI("http://localhost"));
      link.setRel("edit");
      feed.getLinks().add(link);
      feed.getAuthors().add(new Person("John Brown"));
      Entry entry = new Entry();
      entry.setTitle("Hello World");
      Content content = new Content();
      content.setType(MediaType.TEXT_HTML_TYPE);
      content.setText("Nothing much");
      entry.setContent(content);
      feed.getEntries().add(entry);
      return feed;
   }
}

2.7.12.1. Atom プロバイダーでの JAXB の使用

org.jboss.resteasy.plugins.providers.atom.Content クラスを使用すると、コンテンツのボディーであるアノテーション付きのオブジェクトのマーシャリングおよびマーシャリング解除できます。

例: カスタマーのエントリー

@XmlRootElement(namespace = "http://jboss.org/Customer")
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
   @XmlElement
   private String name;

   public Customer() {
   }

   public Customer(String name) {
      this.name = name;
   }

   public String getName() {
      return name;
   }
}

@Path("atom")
public static class AtomServer {
   @GET
   @Path("entry")
   @Produces("application/atom+xml")
   public Entry getEntry() {
      Entry entry = new Entry();
      entry.setTitle("Hello World");
      Content content = new Content();
      content.setJAXBObject(new Customer("bill"));
      entry.setContent(content);
      return entry;
   }
}

Content.setJAXBObject() メソッドを使用すると、適切にマーシャリングするコンテンツオブジェクトを指定できます。XML とは異なるベース形式 (application/atom+json) を使用している場合、添付の Mission オブジェクトは同じ形式でマーシャリングされます。Atom ドキュメントを入力として用意している場合は、Content.getJAXBObject(Class clazz) メソッドを使用して Content から正確な JAXB オブジェクトを抽出することもできます。

例: カスタマーオブジェクトを抽出する Atom ドキュメント

@Path("atom")
public static class AtomServer {
   @PUT
   @Path("entry")
   @Produces("application/atom+xml")
   public void putCustomer(Entry entry) {
      Content content = entry.getContent();
      Customer cust = content.getJAXBObject(Customer.class);
   }
}

2.7.13. YAML プロバイダー

警告

resteasy-yaml-provider モジュールはサポートされません。RESTEasy のマーシャリング解除に使用される SnakeYAML ライブラリーでセキュリティー上の問題があるため、この使用は推奨されません。

RESTEasy には、SnakeYAML ライブラリーを使用した YAML のサポートが同梱されています。

JBoss EAP 7.1 より前のリリースでは、YAML プロバイダー設定はデフォルトで有効にされていました。そのため、YAML がアプリケーションでこれを使用するよう Maven 依存関係を設定することのみが必要でした。JBoss EAP 7.1 より、YAML プロバイダーはデフォルトで無効になり、アプリケーションで明示的に有効にする必要があります。

YAML プロバイダーの有効化

アプリケーションで YAML プロバイダーを有効にするには、以下の手順を実行します。

  1. javax.ws.rs.ext.Providers という名前のファイルを作成または更新します。
  2. 以下の内容をファイルに追加します。

    org.jboss.resteasy.plugins.providers.YamlProvider
  3. WAR または JAR ファイルの META-INF/services/ フォルダーにファイルを配置します。
YAML プロバイダー Maven の依存関係

アプリケーションで YAML プロバイダーを使用するには、アプリケーションのプロジェクト POM ファイルに snakeyaml JAR 依存関係を追加する必要があります。

例: YAML の Maven 依存関係

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-yaml-provider</artifactId>
    <version>${version.org.jboss.resteasy}</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>${version.org.yaml.snakeyaml}</version>
</dependency>

YAML プロバイダーコードの例

YAML プロバイダーは以下の mime タイプを認識します。

  • text/x-yaml
  • text/yaml
  • application/x-yaml

以下は、リソースメソッドで YAML を使用する方法の例になります。

例: リソース生成 YAML

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/yaml")
public class YamlResource {

  @GET
  @Produces("text/x-yaml")
  public MyObject getMyObject() {
    return createMyObject();
  }
...
}