Support for custom status in ResponseStatusException
Closes gh-20336
This commit is contained in:
parent
4d7418841c
commit
37366e0c91
|
@ -36,7 +36,7 @@ import org.springframework.util.Assert;
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class ResponseStatusException extends NestedRuntimeException {
|
public class ResponseStatusException extends NestedRuntimeException {
|
||||||
|
|
||||||
private final HttpStatus status;
|
private final int status;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String reason;
|
private final String reason;
|
||||||
|
@ -70,15 +70,44 @@ public class ResponseStatusException extends NestedRuntimeException {
|
||||||
public ResponseStatusException(HttpStatus status, @Nullable String reason, @Nullable Throwable cause) {
|
public ResponseStatusException(HttpStatus status, @Nullable String reason, @Nullable Throwable cause) {
|
||||||
super(null, cause);
|
super(null, cause);
|
||||||
Assert.notNull(status, "HttpStatus is required");
|
Assert.notNull(status, "HttpStatus is required");
|
||||||
this.status = status;
|
this.status = status.value();
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor with a response status and a reason to add to the exception
|
||||||
|
* message as explanation, as well as a nested exception.
|
||||||
|
* @param rawStatusCode the HTTP status code value
|
||||||
|
* @param reason the associated reason (optional)
|
||||||
|
* @param cause a nested exception (optional)
|
||||||
|
* @since 5.3
|
||||||
|
*/
|
||||||
|
public ResponseStatusException(int rawStatusCode, @Nullable String reason, @Nullable Throwable cause) {
|
||||||
|
super(null, cause);
|
||||||
|
this.status = rawStatusCode;
|
||||||
this.reason = reason;
|
this.reason = reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the HTTP status associated with this exception.
|
* Return the HTTP status associated with this exception.
|
||||||
|
* @throws IllegalArgumentException in case of an unknown HTTP status code
|
||||||
|
* @since #getRawStatusCode()
|
||||||
|
* @see HttpStatus#valueOf(int)
|
||||||
*/
|
*/
|
||||||
public HttpStatus getStatus() {
|
public HttpStatus getStatus() {
|
||||||
|
return HttpStatus.valueOf(this.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the HTTP status code (potentially non-standard and not resolvable
|
||||||
|
* through the {@link HttpStatus} enum) as an integer.
|
||||||
|
* @return the HTTP status as an integer value
|
||||||
|
* @since 5.3
|
||||||
|
* @see #getStatus()
|
||||||
|
* @see HttpStatus#resolve(int)
|
||||||
|
*/
|
||||||
|
public int getRawStatusCode() {
|
||||||
return this.status;
|
return this.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +150,8 @@ public class ResponseStatusException extends NestedRuntimeException {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
String msg = this.status + (this.reason != null ? " \"" + this.reason + "\"" : "");
|
HttpStatus code = HttpStatus.resolve(this.status);
|
||||||
|
String msg = (code != null ? code : this.status) + (this.reason != null ? " \"" + this.reason + "\"" : "");
|
||||||
return NestedExceptionUtils.buildMessage(msg, getCause());
|
return NestedExceptionUtils.buildMessage(msg, getCause());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,9 +88,10 @@ public class ResponseStatusExceptionHandler implements WebExceptionHandler {
|
||||||
|
|
||||||
private boolean updateResponse(ServerHttpResponse response, Throwable ex) {
|
private boolean updateResponse(ServerHttpResponse response, Throwable ex) {
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
HttpStatus status = determineStatus(ex);
|
HttpStatus httpStatus = determineStatus(ex);
|
||||||
if (status != null) {
|
int code = (httpStatus != null ? httpStatus.value() : determineRawStatusCode(ex));
|
||||||
if (response.setStatusCode(status)) {
|
if (code != -1) {
|
||||||
|
if (response.setRawStatusCode(code)) {
|
||||||
if (ex instanceof ResponseStatusException) {
|
if (ex instanceof ResponseStatusException) {
|
||||||
((ResponseStatusException) ex).getResponseHeaders()
|
((ResponseStatusException) ex).getResponseHeaders()
|
||||||
.forEach((name, values) ->
|
.forEach((name, values) ->
|
||||||
|
@ -109,17 +110,30 @@ public class ResponseStatusExceptionHandler implements WebExceptionHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the HTTP status implied by the given exception.
|
* Determine the HTTP status for the given exception.
|
||||||
* @param ex the exception to introspect
|
* <p>As of 5.3 this method always returns {@code null} in which case
|
||||||
|
* {@link #determineRawStatusCode(Throwable)} is used instead.
|
||||||
|
* @param ex the exception to check
|
||||||
* @return the associated HTTP status, if any
|
* @return the associated HTTP status, if any
|
||||||
* @since 5.0.5
|
* @deprecated as of 5.3 in favor of {@link #determineRawStatusCode(Throwable)}.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@Deprecated
|
||||||
protected HttpStatus determineStatus(Throwable ex) {
|
protected HttpStatus determineStatus(Throwable ex) {
|
||||||
if (ex instanceof ResponseStatusException) {
|
|
||||||
return ((ResponseStatusException) ex).getStatus();
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the raw status code for the given exception.
|
||||||
|
* @param ex the exception to check
|
||||||
|
* @return the associated HTTP status code, or -1 if it can't be derived.
|
||||||
|
* @since 5.3
|
||||||
|
*/
|
||||||
|
protected int determineRawStatusCode(Throwable ex) {
|
||||||
|
if (ex instanceof ResponseStatusException) {
|
||||||
|
return ((ResponseStatusException) ex).getRawStatusCode();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2018 the original author or authors.
|
* Copyright 2002-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -17,8 +17,6 @@
|
||||||
package org.springframework.web.reactive.handler;
|
package org.springframework.web.reactive.handler;
|
||||||
|
|
||||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.lang.Nullable;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
import org.springframework.web.server.handler.ResponseStatusExceptionHandler;
|
import org.springframework.web.server.handler.ResponseStatusExceptionHandler;
|
||||||
|
|
||||||
|
@ -39,13 +37,12 @@ import org.springframework.web.server.handler.ResponseStatusExceptionHandler;
|
||||||
public class WebFluxResponseStatusExceptionHandler extends ResponseStatusExceptionHandler {
|
public class WebFluxResponseStatusExceptionHandler extends ResponseStatusExceptionHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
protected int determineRawStatusCode(Throwable ex) {
|
||||||
protected HttpStatus determineStatus(Throwable ex) {
|
int status = super.determineRawStatusCode(ex);
|
||||||
HttpStatus status = super.determineStatus(ex);
|
if (status == -1) {
|
||||||
if (status == null) {
|
|
||||||
ResponseStatus ann = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
|
ResponseStatus ann = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
|
||||||
if (ann != null) {
|
if (ann != null) {
|
||||||
status = ann.code();
|
status = ann.code().value();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
|
|
|
@ -275,7 +275,7 @@ public class ResourceWebHandlerTests {
|
||||||
StepVerifier.create(handler.handle(exchange))
|
StepVerifier.create(handler.handle(exchange))
|
||||||
.expectErrorSatisfies(err -> {
|
.expectErrorSatisfies(err -> {
|
||||||
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
||||||
assertThat(((ResponseStatusException) err).getStatus()).isEqualTo(HttpStatus.NOT_FOUND);
|
assertThat(((ResponseStatusException) err).getRawStatusCode()).isEqualTo(404);
|
||||||
}).verify(TIMEOUT);
|
}).verify(TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,7 +321,7 @@ public class ResourceWebHandlerTests {
|
||||||
StepVerifier.create(this.handler.handle(exchange))
|
StepVerifier.create(this.handler.handle(exchange))
|
||||||
.expectErrorSatisfies(err -> {
|
.expectErrorSatisfies(err -> {
|
||||||
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
||||||
assertThat(((ResponseStatusException) err).getStatus()).isEqualTo(HttpStatus.NOT_FOUND);
|
assertThat(((ResponseStatusException) err).getRawStatusCode()).isEqualTo(404);
|
||||||
})
|
})
|
||||||
.verify(TIMEOUT);
|
.verify(TIMEOUT);
|
||||||
if (!location.createRelative(requestPath).exists() && !requestPath.contains(":")) {
|
if (!location.createRelative(requestPath).exists() && !requestPath.contains(":")) {
|
||||||
|
@ -416,7 +416,7 @@ public class ResourceWebHandlerTests {
|
||||||
StepVerifier.create(this.handler.handle(exchange))
|
StepVerifier.create(this.handler.handle(exchange))
|
||||||
.expectErrorSatisfies(err -> {
|
.expectErrorSatisfies(err -> {
|
||||||
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
||||||
assertThat(((ResponseStatusException) err).getStatus()).isEqualTo(HttpStatus.NOT_FOUND);
|
assertThat(((ResponseStatusException) err).getRawStatusCode()).isEqualTo(404);
|
||||||
}).verify(TIMEOUT);
|
}).verify(TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,7 +427,7 @@ public class ResourceWebHandlerTests {
|
||||||
StepVerifier.create(this.handler.handle(exchange))
|
StepVerifier.create(this.handler.handle(exchange))
|
||||||
.expectErrorSatisfies(err -> {
|
.expectErrorSatisfies(err -> {
|
||||||
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
||||||
assertThat(((ResponseStatusException) err).getStatus()).isEqualTo(HttpStatus.NOT_FOUND);
|
assertThat(((ResponseStatusException) err).getRawStatusCode()).isEqualTo(404);
|
||||||
}).verify(TIMEOUT);
|
}).verify(TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,7 +438,7 @@ public class ResourceWebHandlerTests {
|
||||||
StepVerifier.create(this.handler.handle(exchange))
|
StepVerifier.create(this.handler.handle(exchange))
|
||||||
.expectErrorSatisfies(err -> {
|
.expectErrorSatisfies(err -> {
|
||||||
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
||||||
assertThat(((ResponseStatusException) err).getStatus()).isEqualTo(HttpStatus.NOT_FOUND);
|
assertThat(((ResponseStatusException) err).getRawStatusCode()).isEqualTo(404);
|
||||||
}).verify(TIMEOUT);
|
}).verify(TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,7 +473,7 @@ public class ResourceWebHandlerTests {
|
||||||
StepVerifier.create(mono)
|
StepVerifier.create(mono)
|
||||||
.expectErrorSatisfies(err -> {
|
.expectErrorSatisfies(err -> {
|
||||||
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
||||||
assertThat(((ResponseStatusException) err).getStatus()).isEqualTo(HttpStatus.NOT_FOUND);
|
assertThat(((ResponseStatusException) err).getRawStatusCode()).isEqualTo(404);
|
||||||
}).verify(TIMEOUT);
|
}).verify(TIMEOUT);
|
||||||
|
|
||||||
// SPR-17475
|
// SPR-17475
|
||||||
|
|
|
@ -133,9 +133,7 @@ public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionRes
|
||||||
ex.getResponseHeaders().forEach((name, values) ->
|
ex.getResponseHeaders().forEach((name, values) ->
|
||||||
values.forEach(value -> response.addHeader(name, value)));
|
values.forEach(value -> response.addHeader(name, value)));
|
||||||
|
|
||||||
int statusCode = ex.getStatus().value();
|
return applyStatusAndReason(ex.getRawStatusCode(), ex.getReason(), response);
|
||||||
String reason = ex.getReason();
|
|
||||||
return applyStatusAndReason(statusCode, reason, response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue