New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
URL and HTTP method in rest client exception message #26480
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,11 +20,13 @@ | |
import java.io.IOException; | ||
import java.io.InputStreamReader; | ||
import java.io.Reader; | ||
import java.net.URI; | ||
import java.nio.CharBuffer; | ||
import java.nio.charset.Charset; | ||
import java.nio.charset.StandardCharsets; | ||
|
||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpMethod; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.client.ClientHttpResponse; | ||
|
@@ -109,20 +111,40 @@ protected boolean hasError(int unknownStatusCode) { | |
* {@link HttpStatus} enum range. | ||
* </ul> | ||
* @throws UnknownHttpStatusCodeException in case of an unresolvable status code | ||
* @see #handleError(ClientHttpResponse, HttpStatus) | ||
* @see #handleError(URI, HttpMethod, ClientHttpResponse, HttpStatus) | ||
*/ | ||
@Override | ||
public void handleError(ClientHttpResponse response) throws IOException { | ||
handleError(null, null, response); | ||
} | ||
|
||
/** | ||
* Handle the error in the given response with the given resolved status code. | ||
* <p>The default implementation throws: | ||
* <ul> | ||
* <li>{@link HttpClientErrorException} if the status code is in the 4xx | ||
* series, or one of its sub-classes such as | ||
* {@link HttpClientErrorException.BadRequest} and others. | ||
* <li>{@link HttpServerErrorException} if the status code is in the 5xx | ||
* series, or one of its sub-classes such as | ||
* {@link HttpServerErrorException.InternalServerError} and others. | ||
* <li>{@link UnknownHttpStatusCodeException} for error status codes not in the | ||
* {@link HttpStatus} enum range. | ||
* </ul> | ||
* @throws UnknownHttpStatusCodeException in case of an unresolvable status code | ||
* @see #handleError(URI, HttpMethod, ClientHttpResponse, HttpStatus) | ||
*/ | ||
@Override | ||
public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { | ||
HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode()); | ||
if (statusCode == null) { | ||
byte[] body = getResponseBody(response); | ||
String message = getErrorMessage(response.getRawStatusCode(), | ||
response.getStatusText(), body, getCharset(response)); | ||
String message = getErrorMessage(response.getRawStatusCode(), response.getStatusText(), body, getCharset(response), url, method); | ||
throw new UnknownHttpStatusCodeException(message, | ||
response.getRawStatusCode(), response.getStatusText(), | ||
response.getHeaders(), body, getCharset(response)); | ||
} | ||
handleError(response, statusCode); | ||
handleError(url, method, response, statusCode); | ||
} | ||
|
||
/** | ||
|
@@ -132,9 +154,10 @@ public void handleError(ClientHttpResponse response) throws IOException { | |
* </pre> | ||
*/ | ||
private String getErrorMessage( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally, I'd make this method protected - so that I can override it to extract some more specific information from responses that return HTML, XML or some other nonstandard data. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Considering something beyond 5.3.x - what about this one? I'd love to have an extension point where I can further customize the response body preview - format it right, remove some tags, etc. A long time ago, here: https://github.com/spring-projects/spring-framework/pull/1956/files - I even thought about a separate class to extract the body. But a protected method would be just fine. |
||
int rawStatusCode, String statusText, @Nullable byte[] responseBody, @Nullable Charset charset) { | ||
int rawStatusCode, String statusText, @Nullable byte[] responseBody, @Nullable Charset charset, @Nullable URI url, @Nullable HttpMethod method) { | ||
|
||
String preface = getPreface(rawStatusCode, statusText, url, method); | ||
|
||
String preface = rawStatusCode + " " + statusText + ": "; | ||
if (ObjectUtils.isEmpty(responseBody)) { | ||
return preface + "[no body]"; | ||
} | ||
|
@@ -162,6 +185,15 @@ private String getErrorMessage( | |
} | ||
} | ||
|
||
private String getPreface(int rawStatusCode, String statusText, @Nullable URI url, @Nullable HttpMethod method) { | ||
StringBuilder preface = new StringBuilder(rawStatusCode + " " + statusText); | ||
if (!ObjectUtils.isEmpty(method) && !ObjectUtils.isEmpty(url)) { | ||
preface.append(" after ").append(method).append(" ").append(url).append(" "); | ||
} | ||
preface.append(": "); | ||
return preface.toString(); | ||
} | ||
|
||
/** | ||
* Handle the error based on the resolved status code. | ||
* | ||
|
@@ -175,11 +207,29 @@ private String getErrorMessage( | |
* @see HttpServerErrorException#create | ||
*/ | ||
protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException { | ||
handleError(null, null, response, statusCode); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a protected method that is now bypassed. Something to consider for a minor/major release but not at this stage of the 5.3.x branch. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for looking into that! Initially, I didn't think of squeezing it into 5.3. I'd be very happy to do so - but I think the previous changes broke something in spring-integration, so it might not be that easy. I tried to not to change the behaviour of this method at all. I can also try to rewrite this to make it more 5.3-compatible. But If you think it makes more sense to wait - let's wait. |
||
|
||
/** | ||
* Handle the error based on the resolved status code. | ||
* | ||
* <p>The default implementation delegates to | ||
* {@link HttpClientErrorException#create} for errors in the 4xx range, to | ||
* {@link HttpServerErrorException#create} for errors in the 5xx range, | ||
* or otherwise raises {@link UnknownHttpStatusCodeException}. | ||
* | ||
* @since 5.0 | ||
* @see HttpClientErrorException#create | ||
* @see HttpServerErrorException#create | ||
*/ | ||
protected void handleError(@Nullable URI url, @Nullable HttpMethod method, ClientHttpResponse response, | ||
HttpStatus statusCode) throws IOException { | ||
|
||
String statusText = response.getStatusText(); | ||
HttpHeaders headers = response.getHeaders(); | ||
byte[] body = getResponseBody(response); | ||
Charset charset = getCharset(response); | ||
String message = getErrorMessage(statusCode.value(), statusText, body, charset); | ||
String message = getErrorMessage(statusCode.value(), statusText, body, charset, url, method); | ||
|
||
switch (statusCode.series()) { | ||
case CLIENT_ERROR: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the protected method that's now bypassed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation!
Then it can definitely wait for a future Spring release. I'm not in a particular hurry - any custom extensions would well enough.
Should I close the PR, or just keep it here?
Also - is it something that you'd see easy to port into
WebClient
? Would it make sense to make these two in sync?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can close it for now. On the WebClient side it looks we do have the URL and HTTP method in
WebClientResponseException
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback @rstoyanchev !
Is there a chance to squeeze this in a future Spring Framework release? Should I come back once it's announced?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, of course. We can probably re-open this PR then.