web-dev-qa-db-ja.com

HTTPリクエストのステータスが401の場合、Javaでレスポンス本文を解析する方法

SpringのRestTemplateとJacksonを使用してRESTful JSON APIを使用しています。場合によっては、Status 401(認証されていない)応答、カスタムJSONボディ、これはAPIメーカーによって定義され、次のようになります。

{
    "code": 123,
    "message": "Reason for the error"
}

本文を解析し、ビジネスロジックでcodeプロパティを使用する必要があります。

これはエラー応答ですJava解析する必要があるオブジェクト:

public class CustomError {

    @JsonProperty
    private Integer code;
    @JsonProperty
    private String message;

    public Integer getCode() {
       return code;
    }
    public String getMessage() {
        return message;
    }
}

これを行うカスタムエラーハンドラ:

public class CustomErrorHandler extends DefaultResponseErrorHandler {
    private RestTemplate restTemplate;
    private ObjectMapper objectMapper;
    private MappingJacksonHttpMessageConverter messageConverter;


    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        return super.hasError(response);
    }

    @Override
    public void handleError(final ClientHttpResponse response) throws IOException {

        try {
            CustomError error = 
                (CustomError) messageConverter.read(CustomError.class, response);
            throw new CustomErrorIOException(error, error.getMessage());
        } catch (Exception e) {
            // parsing failed, resort to default behavior
            super.handleError(response);
        }
    }
}

エラーハンドラーは、tryブロックでHttpMessageNotReadableExceptionで失敗します。

「JSONを読み取れませんでした:サーバー認証のため、ストリーミングモードで再試行できません」

これは私がリクエストを送信する方法です:

restTemplate.postForObject(url, pojoInstance, responseClass);

同じ要求がPostmanなどの単純な古いRESTクライアントプログラムで実行された場合、予想されるJSON応答が受信されます。そのため、401ステータスの場合、SpringのClientHttpResponse実装が何らかの形で応答本文へのアクセスを許可しない可能性があると考えられます。

応答本文を解析することは実際に可能ですか?

更新

私が調べたところから、RestTemplateクラスはClientHttpResponseを使用し、これがSun.net.www.protocol.http.HttpURLConnection入力ストリームを提供します。入力ストリームが無視され、IOExceptionがスローされる場所があります。

サーバー認証のため、ストリーミングモードで再試行できません

そのため、HttpURLConnectionの実装が問題を引き起こしています。

この問題を回避することは可能ですか?おそらく、エラーステータスコードの場合に応答本文を無視しない代替実装を使用する必要がありますか?他の選択肢を推奨できますか?

20
Ivaylo Slavov

カスタムハンドラを必要とせずに次のアプローチを試してください。これは、HttpStatusCodeExceptionから文字列として応答を取得し、それをオブジェクトに変換するという考え方です。変換には、ジャクソンのObjectMapperを使用しました。

        try {

            restTemplate.postForObject(url, pojoInstance, responseClass);

        } catch (HttpStatusCodeException e) {

            if (e.getStatusCode() == HttpStatus.UNAUTHORIZED) {

                String responseString = e.getResponseBodyAsString();

                ObjectMapper mapper = new ObjectMapper();

                CustomError result = mapper.readValue(responseString,
                        CustomError.class);
            }
        }

更新:問題に関連するデフォルトのバグがあるため、別のファクトリの使用も役立つ場合があります(以下のコメントを参照)。

RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
37
Marios