web-dev-qa-db-ja.com

Spring RestTemplate URLエンコーディング

私はsprings resttemplateで簡単な休憩呼び出しをしようとします:

private void doLogout(String endpointUrl, String sessionId) {
    template.getForObject("http://{enpointUrl}?method=logout&session={sessionId}", Object.class,
            endpointUrl, sessionId);
}

EndpointUrl変数にservice.Host.com/api/service.phpのようなものが含まれている場合

残念ながら、私の呼び出しはorg.springframework.web.client.ResourceAccessException:I/O error:service.Host.com%2Fapi%2Fservice.phpを引き起こします

したがって、Springは、URLの作成中に前に私のendpointUrl文字列をエンコードするようです。春がこれをしないようにする簡単な方法はありますか?

よろしく

14
user1145874

これを行う簡単な方法はありません。 URIテンプレート変数は通常、パス要素またはクエリ文字列パラメーター用です。ホストを渡そうとしています。理想的には、URIを構築するためのより良い解決策を見つけるでしょう。 Yuci's solution をお勧めします。

Springユーティリティとテンプレート拡張を引き続き使用する場合、1つの回避策は、UriTemplateを使用して、URI変数を含むURLを生成し、URLデコードしてRestTemplate

String url = "http://{enpointUrl}?method=logout&session={sessionId}";
URI expanded = new UriTemplate(url).expand(endpointUrl, sessionId); // this is what RestTemplate uses 
url = URLDecoder.decode(expanded.toString(), "UTF-8"); // Java.net class
template.getForObject(url, Object.class);
13

使用しているSpringのバージョンによって異なります。バージョンが古すぎる場合、たとえば、バージョン3.0.6.RELEASEの場合、UriComponentsBuilderなどの機能はありません。あなたの春のウェブ瓶。

Spring RestTemplateがURLをエンコードしないようにする必要があります。あなたができることは:

_import Java.net.URI;

StringBuilder builder = new StringBuilder("http://");
builder.append(endpointUrl);
builder.append("?method=logout&session=");
builder.append(sessionId);

URI uri = URI.create(builder.toString());
restTemplate.getForObject(uri, Object.class);
_

Springバージョン3.0.6.RELEASEでテストしましたが、動作しました。

Wordでは、restTemplate.getForObject(String url, Object.class)を使用する代わりにrestTemplate.getForObject(Java.net.URI uri, Object.class)を使用します

Springドキュメントのrest-resttemplate-uriセクションを参照してください

12
Yuci

代わりに、Java.net.URIを受け取るオーバーロードされたバリアントを使用できます。public T getForObject(URI url、Class responseType)throws RestClientException

から Spring自身のドキュメント

UriComponents uriComponents =
    UriComponentsBuilder.fromUriString("http://example.com/hotels/{hotel}/bookings/{booking}").build()
        .expand("42", "21")
        .encode();

URI uri = uriComponents.toUri();
5

私は最高のネイティブな方法(最新)の解決策を見つけたようです:

  1. エンコードされたURL文字列をパラメータとしてRestTemplate.exchange()に渡さないでください
  2. 代わりにURIオブジェクトを使用してください。 UriComponentsBuilderを使用してURIを構築します。

以下の(簡略化された)例を参照してください。

    String instanceUrl = "https://abc.my.salesforce.com"
    HttpEntity<String> entity = new HttpEntity<>(headers);
    UriComponents uriComponents =
            UriComponentsBuilder.fromHttpUrl(instanceUrl)
                    .path("/services/data/v45.0/query/")
                    .queryParam("q", String.format(sqlSelect, id))
                    .build();

    ResponseEntity<OpportunityLineItem> responseEntity =
            restTemplate.exchange(
                    uriComponents.toUri(), HttpMethod.GET,
                    entity, OpportunityLineItem.class);

// Wrong! URI string will be double encoded
/*
ResponseEntity<OpportunityLineItem> responseEntity =
            restTemplate.exchange(
                    uriComponents.toUriString(), HttpMethod.GET,
                    entity, OpportunityLineItem.class);
*/

この方法では、二重エンコーディングで問題が発生しません。

Spring RestTemplateクライアント(SOQLクエリを含む)に基づいて、SalesForce RESTクライアントをデバッグしているときにソリューションが見つかりました。

3
Dmitry Trifonov

どうやらこれを行うには、クラスUriComponentsBuilderのbuild(true)を呼び出すより良い方法があります。

private void doLogout(String endpointUrl, String sessionId) {
    String url = "http://" + endpointUrl +"?method=logout&session=" + + URLEncoder.encode(sessionId, "UTF-8");
    URI uri = UriComponentsBuilder.fromUriString(url.toString()).build(true).toUri();
    template.getForObject(uri, Object.class,
            endpointUrl, sessionId);
}

このメソッドは、URIの作成中にエンコードしないようにURIComponentsBuilderに指示します。

1
mtk.94

HttpMethodおよびResponseTypeのヘッダー、本文を含む完全な例は次のようになります。

String url = "http://google.com/{path}?param1={param1Value}&param2={param2Value}";
Object body = null;
HttpEntity request = new HttpEntity(body, new HttpHeaders());

Map<String, String> uriVariables = new HashMap<>();
uriVariables.put("path", "search");
uriVariables.put("param1Value", "value1");
uriVariables.put("param2Value", "value2");

ResponseEntity<Void> responseEntity = restTemplate.exchange(url, HttpMethod.POST, request, Void.class, uriVariables)
//responseEntity.getBody()

実際には、同じUriTemplateとexpandメソッドを使用します

1
Geniy