4

Jersey クライアント API を使用して、サードパーティの REST サービスを利用しようとしています。自動 POJO デシリアライゼーションを使用して、JSON 応答から Java オブジェクトに移行する予定です。

残念ながら、サード パーティ サービスは content type を使用して応答を返します"text/javascript"。私のジャージー クライアントは、これを JSON オブジェクトと見なす必要があることを理解できず、オブジェクトのデシリアライズに失敗します。

簡単なジャージー サーバー アプリケーションを作成して、コンテンツ タイプを から"text/javascript"に変更する"application/json"ことで逆シリアル化が機能することを確認しました。

この情報を利用して、Jersey クライアント フィルターを使用して応答ヘッダーを変更することにしました。コードは、この質問の作成者によるコメントからのものです。実際、質問は私のものとまったく同じように見えますが、回答者は質問に誤って答え、 (応答ヘッダーではなく)要求ヘッダーを変更する方法を示しています。元の作成者はその回答を使用して解決策を作成できましたが、彼が述べた解決策は機能しないようです。

フィルタ コードは次のとおりです。

client.addFilter(new ClientFilter() {
  @Override public ClientResponse handle(ClientRequest cr) 
      throws ClientHandlerException {
    ClientResponse response = getNext().handle(cr); 
    response.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); 
    return response;
  }
});

ただし、実行されると、 anUnsupportedOperationExceptionが発生します。

Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableCollection.clear(Collections.java:1035)
at com.sun.jersey.core.util.StringKeyIgnoreCaseMultivaluedMap.putSingle(StringKeyIgnoreCaseMultivaluedMap.java:78)
at com.sun.jersey.core.util.StringKeyIgnoreCaseMultivaluedMap.putSingle(StringKeyIgnoreCaseMultivaluedMap.java:56)
at App$1.handle(App.java:49)
at com.sun.jersey.api.client.Client.handle(Client.java:648)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:680)
at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:507)
at App.main(App.java:63)

返されたヘッダーは、変更不可能なコレクションにラップされているように見えます。

次に、すべてのヘッダーを新しいコレクションにコピーしようとしましたが、ヘッダーのマップを応答に戻す方法がわかりません

ClientResponse最後に、修正したヘッダーを含む新しいを作成できるのではないかと考えました。ただし、コンストラクターにClientResponseは次のシグネチャがあります。

public ClientResponse(int status, 
                      InBoundHeaders headers, 
                      InputStream entity, 
                      MessageBodyWorkers workers)  

statusheadersおよびentity変数をオリジナルからコピーするのは簡単です。workersただし、フィールドへの参照を取得する方法がわかりません。

POJO デシリアライゼーションが機能するように、Jersey クライアント フィルターを使用して から"text/javascript"への応答ヘッダーを変更するにはどうすればよいですか?"application/json"

4

5 に答える 5

8

Jersey 2 では、着信応答の HTTP ヘッダーを操作するために、ClientResponseFilterの実装をClientConfig に登録します。

たとえば、これはHTTPヘッダーを操作するためにJersey 2.3.1でうまく機能するようです:

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.client.ClientRequestContext;

import org.glassfish.jersey.client.ClientConfig;

/* Ensure that there is an "application/xml" Content-Type header on
 * successful responses without a content type header. */
@Provider
public static class EnsureXmlContentTypeFilter implements ClientResponseFilter {
    @Override
    public void filter(ClientRequestContext requestContext,
                       ClientResponseContext responseContext) {
        if (200 == responseContext.getStatus() &&
            null == responseContext.getHeaderString(HttpHeaders.CONTENT_TYPE)) {

            responseContext.getHeaders().add(
                HttpHeaders.CONTENT_TYPE, "application/xml"
            );

        }
    }
}

private final ClientConfig config = new ClientConfig()
    // Registering this filter adds a "Content-Type: application/xml" 
    // header to each response that lacks Content-Type headers.
    .register(EnsureXmlContentTypeFilter.class)
;
private final Client client = ClientBuilder.newClient(config);

フィルターとインターセプターに関するジャージーのドキュメントは完全ではありませんが、関連するクラスの javadoc へのリンクがいくつかあります: https://jersey.java.net/documentation/latest/filters-and-interceptors.html

XML コンテンツで応答するサービスから XML 応答を取得していますが、「Content-Type: application/xml」ヘッダーがありません。おそらく、より良いアプローチは MessageBodyReaders を登録することですが、上記のアプローチは、そのサービスの API をいじっている間は機能します。

于 2013-10-24T15:57:13.847 に答える
3

あなたの本当の質問に対する答えはありませんが、フィルターで新しい応答を作成しようとする場合に、そのワーカー インスタンスを取得する方法がわかると思います。

必要な「workers」オブジェクトはシングルトンのようです。com.sun.jersey.api.client.Client インスタンスを取得できれば、workers オブジェクトを取得できます。私の場合、Jersey クライアント コードは、JerseyTest をサブクラス化した単体テスト内にあります。JerseyTest は、Client オブジェクトを返すメソッド "client()" を定義します。次のテストコードを追加しました(正確ではありませんが、近いです):

  MessageBodyWorkers workers = client().getMessageBodyWorkers();

次に、ClientResponse のコンストラクターにブレークポイントを設定します (これは、Jersey によって返された元の ClientResponse です。テストには必要ないため、複製は試みていません)。コンストラクターに渡されたワーカーは同じインスタンスでした。したがって、応答オブジェクトからワーカー オブジェクトを取得できなくても、別の場所で取得できるはずです。

于 2012-10-05T02:07:13.447 に答える
3

Guido の回答は、新しいClientResponseオブジェクトを作成して代わりに返すために必要な洞察を提供します。まだ追跡していない理由により、新しい を作成しInboundHeaders、既存のすべてのヘッダーをそれに追加してから、問題の単一のヘッダーを変更しても、UnsupportedOperationException. そのため、ヘッダーを書き直すには、元のヘッダーを繰り返し処理し、正しいセットを繰り返し構築します。

final Client client = Client.create(clientConfig);
client.addFilter(new ClientFilter()
{
  @Override
  public ClientResponse handle(ClientRequest cr) throws ClientHandlerException
  {
    final ClientResponse response = getNext().handle(cr); 
    final InBoundHeaders headers = new InBoundHeaders();

    for (String header : response.getHeaders().keySet())
    {
      if (header.equals(HttpHeaders.CONTENT_TYPE))
      {
        headers.putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); 
      }
      else
      {
        headers.put(header, headers.get(header));
      }
    }

    return new ClientResponse(response.getStatus(),
                              headers,
                              response.getEntityInputStream(),
                              client.getMessageBodyWorkers());
  }
}
于 2012-10-08T00:50:04.000 に答える
1

Jersey 2 では、ClientResponseFilter. 次に、 を呼び出すだけresponseContext.getHeaders().putSingle(...)です。

Java 8 では、ラムダで実行できます。

client.register((ClientResponseFilter) (requestContext, responseContext) ->
                responseContext.getHeaders().putSingle("Content-Type", "application/json"));
于 2015-09-17T18:40:35.263 に答える
0

既存のフィルタインスタンスを再利用する場合は、Clientではなく に登録するだけClientConfigです。

古い方法 (Jersey-1.9) :

     import com.sun.jersey.api.client.Client;
     import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;

     Client client = new Client();
     client.addFilter(new HTTPBasicAuthFilter(username, password));

新しい方法 (Jersey-2.3):

     import javax.ws.rs.client.Client;
     import javax.ws.rs.client.ClientBuilder;
     import org.glassfish.jersey.client.filter.HttpBasicAuthFilter;

     Client client = ClientBuilder.newClient();
     client.register(new HttpBasicAuthFilter(username, password));

これは最善の解決策ではありませんが、移行に役立つ場合があります。

于 2014-05-17T18:48:57.423 に答える