1. 概要

Feign は、HTTP呼び出しを抽象化し、宣言型にします。 そうすることで、Feignは、HTTP接続管理、ハードコードされたURL、その他のボイラープレートコードなどの下位レベルの詳細を非表示にします。 Feignクライアントを使用することの重要な利点は、HTTP呼び出しが簡単になり、多くのコードが不要になることです。 通常、Feign for REST API application /jsonメディアタイプを使用します。 ただし、Feignクライアントは、 text / xml 、マルチパートリクエストなどの他のメディアタイプでうまく機能します。

このチュートリアルでは、Feignを使用してSOAPベースのWebサービス( text / xml )を呼び出す方法を学びましょう。

2. SOAPWebサービス

getUsercreateUserの2つの操作を持つSOAPWebサービスがあると仮定します。

cURL を使用して、操作createUserを呼び出しましょう。

curl -d @request.xml -i -o -X POST --header 'Content-Type: text/xml'
  http://localhost:18080/ws/users

ここで、request.xmlにはSOAPペイロードが含まれています。

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:feig="http://www.baeldung.com/springbootsoap/feignclient">
    <soapenv:Header/>
    <soapenv:Body>
         <feig:createUserRequest>
             <feig:user>
                 <feig:id>1</feig:id>
                 <feig:name>john doe</feig:name>
                 <feig:email>[email protected]</feig:email>
            </feig:user>
         </feig:createUserRequest>
    </soapenv:Body>
</soapenv:Envelope>

すべての構成が正しい場合、正常な応答が得られます。

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Header/>
    <SOAP-ENV:Body>
        <ns2:createUserResponse xmlns:ns2="http://www.baeldung.com/springbootsoap/feignclient">
            <ns2:message>Success! Created the user with id - 1</ns2:message>
        </ns2:createUserResponse>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

同様に、他の操作であるgetUserもcURLを使用して呼び出すことができます。

3. 依存関係

次に、Feignを使用してこのSOAPWebサービスを呼び出す方法を見てみましょう。 SOAPサービスを呼び出す2つの異なるクライアントを開発してみましょう。 Feignは、 Apache HttpComponents OkHttp java.net.URLなどの複数の既存のHTTPクライアントをサポートします。 基盤となるHTTPクライアントとしてApacheHttpComponentsを使用してみましょう。 まず、 OpenFeign ApacheHttpComponentsの依存関係を追加しましょう。

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-hc5</artifactId>
    <version>11.8</version>
</dependency>

次のセクションでは、Feignを使用してSOAPWebサービスを呼び出すいくつかの方法を学びましょう。

4. プレーンテキストとしてのSOAPオブジェクト

content-typeおよびacceptヘッダーがtext/xmlに設定されたプレーンテキストとしてSOAPリクエストを送信できます。 このアプローチを示すクライアントを開発しましょう。

public interface SoapClient {
    @RequestLine("POST")
    @Headers({"SOAPAction: createUser", "Content-Type: text/xml;charset=UTF-8",
      "Accept: text/xml"})
    String createUserWithPlainText(String soapBody);
}

ここで、createUserWithPlainTextStringSOAPペイロードを受け取ります。 acceptおよびcontent-typeヘッダーを明示的に定義したことに注意してください。 これは、SOAP本文をテキストとして送信する場合、 コンテンツタイプ承認ヘッダーとして text/xml。

このアプローチの欠点の1つは、SOAPペイロードを事前に知っておく必要があることです。 幸い、WSDLが利用可能な場合は、SoapUIなどのオープンソースツールを使用してペイロードを生成できます。 ペイロードの準備ができたら、Feignを使用してSOAPWebサービスを呼び出しましょう。

@Test
void givenSOAPPayload_whenRequest_thenReturnSOAPResponse() throws Exception {
    String successMessage="Success! Created the user with id";
    SoapClient client = Feign.builder()
       .client(new ApacheHttp5Client())
       .target(SoapClient.class, "http://localhost:18080/ws/users/");
    
    assertDoesNotThrow(() -> client.createUserWithPlainText(soapPayload()));
    
    String soapResponse= client.createUserWithPlainText(soapPayload());
 
    assertNotNull(soapResponse);
    assertTrue(soapResponse.contains(successMessage));
}

Feignは、SOAPメッセージおよびその他のHTTP関連情報のロギングをサポートしています。 この情報はデバッグにとって重要です。 それでは、Feignロギングを有効にしましょう。 これらのメッセージのログには、追加のfeign-slf4j依存関係が必要です。

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-slf4j</artifactId>
    <version>11.8</version>
</dependency>

テストケースを拡張して、ログ情報を含めましょう。

SoapClient client = Feign.builder()
  .client(new ApacheHttp5Client())
  .logger(new Slf4jLogger(SoapClient.class))
  .logLevel(Logger.Level.FULL)
  .target(SoapClient.class, "http://localhost:18080/ws/users/");

これで、テストを実行すると、次のようなログが表示されます。

18:01:58.295 [main] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> "SOAPAction: createUser[\r][\n]"
18:01:58.295 [main] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> "<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:feig="http://www.baeldung.com/springbootsoap/feignclient">[\n]"
18:01:58.295 [main] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> " <soapenv:Header/>[\n]"
18:01:58.295 [main] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> " <soapenv:Body>[\n]"
18:01:58.295 [main] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> " <feig:createUserRequest>[\n]"
18:01:58.295 [main] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> " <feig:user>[\n]"
18:01:58.295 [main] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> " <feig:id>1</feig:id>[\n]"
18:01:58.295 [main] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> " <feig:name>john doe</feig:name>[\n]"
18:01:58.296 [main] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> " <feig:email>[email protected]</feig:email>[\n]"
18:01:58.296 [main] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> " </feig:user>[\n]"
18:01:58.296 [main] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> " </feig:createUserRequest>[\n]"
18:01:58.296 [main] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> " </soapenv:Body>[\n]"
18:01:58.296 [main] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> "</soapenv:Envelope>"
18:01:58.300 [main] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><SOAP-ENV:Body><ns2:createUserResponse xmlns:ns2="http://www.baeldung.com/springbootsoap/feignclient"><ns2:message>Success! Created the user with id - 1</ns2:message></ns2:createUserResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>"

5. 偽のSOAPコーデック

SOAP Webサービスを呼び出すためのよりクリーンで優れたアプローチは、FeignのSOAPコーデックを使用することです。 コーデックは、SOAPメッセージのマーシャリング(JavaからSOAP)/アンマーシャリング(SOAPからJava)に役立ちます。 ただし、コーデックには追加のfeign-soap依存関係が必要です。 したがって、この依存関係を宣言しましょう。

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-soap</artifactId>
    <version>11.8</version>
</dependency>

Feign SOAPコーデックは、JAXBおよびSoapMessageを使用してSOAPオブジェクトをエンコードおよびデコードし、JAXBContextFactoryは必要なマーシャラーとアンマーシャラーを提供します。

次に、作成したXSDに基づいて、ドメインクラスを生成しましょう。 JAXBでは、SOAPメッセージをマーシャリングおよびアンマーシャリングするために、これらのドメインクラスが必要です。 まず、pom.xmlにプラグインを追加しましょう。

<plugin>
    <groupId>org.jvnet.jaxb2.maven2</groupId>
    <artifactId>maven-jaxb2-plugin</artifactId>
    <version>0.14.0</version>
    <executions>
        <execution>
            <id>feign-soap-stub-generation</id>
            <phase>compile</phase>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <schemaDirectory>target/generated-sources/jaxb</schemaDirectory>
                <schemaIncludes>
                    <include>*.xsd</include>
                </schemaIncludes>
                <generatePackage>com.baeldung.feign.soap.client</generatePackage>
                <generateDirectory>target/generated-sources/jaxb</generateDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>

ここでは、コンパイルフェーズ中に実行するようにプラグインを構成しました。 それでは、スタブを生成しましょう。

mvn clean compile

ビルドが成功すると、targetフォルダーに次のソースが含まれます。

次に、これらのスタブとFeignを使用してSOAPWebサービスを呼び出しましょう。 ただし、最初に、SOAPClientに新しいメソッドを追加しましょう。

@RequestLine("POST")
@Headers({"Content-Type: text/xml;charset=UTF-8"})
CreateUserResponse createUserWithSoap(CreateUserRequest soapBody);

次に、SOAPWebサービスをテストしてみましょう。

@Test
void whenSoapRequest_thenReturnSoapResponse() {
    JAXBContextFactory jaxbFactory = new JAXBContextFactory.Builder()
      .withMarshallerJAXBEncoding("UTF-8").build();
    SoapClient client = Feign.builder()
      .encoder(new SOAPEncoder(jaxbFactory))
      .decoder(new SOAPDecoder(jaxbFactory))
      .target(SoapClient.class, "http://localhost:18080/ws/users/");
    CreateUserRequest request = new CreateUserRequest();
    User user = new User();
    user.setId("1");
    user.setName("John Doe");
    user.setEmail("john.doe@gmail");
    request.setUser(user);
    CreateUserResponse response = client.createUserWithSoap(request);

    assertNotNull(response);
    assertNotNull(response.getMessage());
    assertTrue(response.getMessage().contains("Success")); 
}

テストケースを拡張して、HTTPメッセージとSOAPメッセージをログに記録しましょう。

SoapClient client = Feign.builder()
  .encoder(new SOAPEncoder(jaxbFactory))
  .errorDecoder(new SOAPErrorDecoder())
  .logger(new Slf4jLogger())
  .logLevel(Logger.Level.FULL)
  .decoder(new SOAPDecoder(jaxbFactory))
  .target(SoapClient.class, "http://localhost:18080/ws/users/");

このコードは、以前に見たのと同様のログを生成します。

最後に、SOAP障害を処理しましょう。 Feignは、SOAP障害をSOAPFaultExceptionとして返すSOAPErrorDecoderを提供します。 それでは、this SOAPErrorDecoder をFeignエラーデコーダーとして設定し、SOAP障害を処理しましょう。

SoapClient client = Feign.builder()
  .encoder(new SOAPEncoder(jaxbFactory))
  .errorDecoder(new SOAPErrorDecoder())  
  .decoder(new SOAPDecoder(jaxbFactory))
  .target(SoapClient.class, "http://localhost:18080/ws/users/");
try {
    client.createUserWithSoap(request);
} catch (SOAPFaultException soapFaultException) {
    assertNotNull(soapFaultException.getMessage());
    assertTrue(soapFaultException.getMessage().contains("This is a reserved user id"));
}

ここで、SOAP WebサービスがSOAP障害をスローした場合、それはSOAPFaultExceptionによって処理されます。

6. 結論

この記事では、Feignを使用してSOAPWebサービスを呼び出す方法を学びました。 Feignは、SOAP /RESTWebサービスを簡単に呼び出すことができる宣言型HTTPクライアントです。 Feignを使用する利点は、コードの行を減らすことです。 コードの行が少ないほど、バグや単体テストが少なくなります。

いつものように、完全なソースコードはGitHubから入手できます。