Support non-standard error codes with AbstractErrorWebExceptionHandler
Fixes gh-16691
This commit is contained in:
parent
a695e062d1
commit
4b4dc28a86
|
|
@ -62,6 +62,7 @@ public abstract class AbstractErrorWebExceptionHandler implements ErrorWebExcept
|
||||||
* Currently duplicated from Spring WebFlux HttpWebHandlerAdapter.
|
* Currently duplicated from Spring WebFlux HttpWebHandlerAdapter.
|
||||||
*/
|
*/
|
||||||
private static final Set<String> DISCONNECTED_CLIENT_EXCEPTIONS;
|
private static final Set<String> DISCONNECTED_CLIENT_EXCEPTIONS;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Set<String> exceptions = new HashSet<>();
|
Set<String> exceptions = new HashSet<>();
|
||||||
exceptions.add("AbortedException");
|
exceptions.add("AbortedException");
|
||||||
|
|
@ -276,7 +277,8 @@ public abstract class AbstractErrorWebExceptionHandler implements ErrorWebExcept
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(request.exchange().getLogPrefix() + formatError(throwable, request));
|
logger.debug(request.exchange().getLogPrefix() + formatError(throwable, request));
|
||||||
}
|
}
|
||||||
if (response.statusCode().equals(HttpStatus.INTERNAL_SERVER_ERROR)) {
|
if (HttpStatus.resolve(response.rawStatusCode()) != null
|
||||||
|
&& response.statusCode().equals(HttpStatus.INTERNAL_SERVER_ERROR)) {
|
||||||
logger.error(request.exchange().getLogPrefix() + "500 Server Error for " + formatRequest(request),
|
logger.error(request.exchange().getLogPrefix() + "500 Server Error for " + formatRequest(request),
|
||||||
throwable);
|
throwable);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.boot.autoconfigure.web.reactive.error;
|
package org.springframework.boot.autoconfigure.web.reactive.error;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -113,16 +114,26 @@ public class DefaultErrorWebExceptionHandler extends AbstractErrorWebExceptionHa
|
||||||
protected Mono<ServerResponse> renderErrorView(ServerRequest request) {
|
protected Mono<ServerResponse> renderErrorView(ServerRequest request) {
|
||||||
boolean includeStackTrace = isIncludeStackTrace(request, MediaType.TEXT_HTML);
|
boolean includeStackTrace = isIncludeStackTrace(request, MediaType.TEXT_HTML);
|
||||||
Map<String, Object> error = getErrorAttributes(request, includeStackTrace);
|
Map<String, Object> error = getErrorAttributes(request, includeStackTrace);
|
||||||
HttpStatus errorStatus = getHttpStatus(error);
|
int errorStatus = getHttpStatus(error);
|
||||||
ServerResponse.BodyBuilder responseBody = ServerResponse.status(errorStatus).contentType(TEXT_HTML_UTF8);
|
ServerResponse.BodyBuilder responseBody = ServerResponse.status(errorStatus).contentType(TEXT_HTML_UTF8);
|
||||||
return Flux
|
return Flux.just(getData(errorStatus).toArray(new String[] {}))
|
||||||
.just("error/" + errorStatus.value(), "error/" + SERIES_VIEWS.get(errorStatus.series()), "error/error")
|
|
||||||
.flatMap((viewName) -> renderErrorView(viewName, responseBody, error))
|
.flatMap((viewName) -> renderErrorView(viewName, responseBody, error))
|
||||||
.switchIfEmpty(this.errorProperties.getWhitelabel().isEnabled()
|
.switchIfEmpty(this.errorProperties.getWhitelabel().isEnabled()
|
||||||
? renderDefaultErrorView(responseBody, error) : Mono.error(getError(request)))
|
? renderDefaultErrorView(responseBody, error) : Mono.error(getError(request)))
|
||||||
.next();
|
.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<String> getData(int errorStatus) {
|
||||||
|
HttpStatus errorHttpStatus = HttpStatus.resolve(errorStatus);
|
||||||
|
List<String> data = new ArrayList<>();
|
||||||
|
data.add("error/" + errorStatus);
|
||||||
|
if (errorHttpStatus != null) {
|
||||||
|
data.add("error/" + SERIES_VIEWS.get(errorHttpStatus.series()));
|
||||||
|
}
|
||||||
|
data.add("error/error");
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the error information as a JSON payload.
|
* Render the error information as a JSON payload.
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
|
|
@ -157,9 +168,8 @@ public class DefaultErrorWebExceptionHandler extends AbstractErrorWebExceptionHa
|
||||||
* @param errorAttributes the current error information
|
* @param errorAttributes the current error information
|
||||||
* @return the error HTTP status
|
* @return the error HTTP status
|
||||||
*/
|
*/
|
||||||
protected HttpStatus getHttpStatus(Map<String, Object> errorAttributes) {
|
protected int getHttpStatus(Map<String, Object> errorAttributes) {
|
||||||
int statusCode = (int) errorAttributes.get("status");
|
return (int) errorAttributes.get("status");
|
||||||
return HttpStatus.valueOf(statusCode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,37 @@
|
||||||
|
|
||||||
package org.springframework.boot.autoconfigure.web.reactive.error;
|
package org.springframework.boot.autoconfigure.web.reactive.error;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
||||||
|
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
||||||
|
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext;
|
||||||
|
import org.springframework.boot.web.reactive.error.ErrorAttributes;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||||
|
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
import org.springframework.web.reactive.result.view.View;
|
||||||
|
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
import org.springframework.web.server.adapter.HttpWebHandlerAdapter;
|
import org.springframework.web.server.adapter.HttpWebHandlerAdapter;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link AbstractErrorWebExceptionHandler}.
|
* Tests for {@link AbstractErrorWebExceptionHandler}.
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
|
* @author Madhura Bhave
|
||||||
*/
|
*/
|
||||||
class DefaultErrorWebExceptionHandlerTests {
|
class DefaultErrorWebExceptionHandlerTests {
|
||||||
|
|
||||||
|
|
@ -39,4 +59,31 @@ class DefaultErrorWebExceptionHandlerTests {
|
||||||
assertThat(errorHandlers).isNotNull().isEqualTo(webHandlers);
|
assertThat(errorHandlers).isNotNull().isEqualTo(webHandlers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nonStandardErrorStatusCodeShouldNotFail() {
|
||||||
|
ErrorAttributes errorAttributes = mock(ErrorAttributes.class);
|
||||||
|
ResourceProperties resourceProperties = new ResourceProperties();
|
||||||
|
ErrorProperties errorProperties = new ErrorProperties();
|
||||||
|
ApplicationContext context = new AnnotationConfigReactiveWebApplicationContext();
|
||||||
|
given(errorAttributes.getErrorAttributes(any(), anyBoolean())).willReturn(getErrorAttributes());
|
||||||
|
DefaultErrorWebExceptionHandler exceptionHandler = new DefaultErrorWebExceptionHandler(errorAttributes,
|
||||||
|
resourceProperties, errorProperties, context);
|
||||||
|
setupViewResolver(exceptionHandler);
|
||||||
|
ServerWebExchange exchange = MockServerWebExchange
|
||||||
|
.from(MockServerHttpRequest.get("/some-other-path").accept(MediaType.TEXT_HTML));
|
||||||
|
exceptionHandler.handle(exchange, new RuntimeException()).block();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> getErrorAttributes() {
|
||||||
|
return Collections.singletonMap("status", 498);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupViewResolver(DefaultErrorWebExceptionHandler exceptionHandler) {
|
||||||
|
View view = mock(View.class);
|
||||||
|
given(view.render(any(), any(), any())).willReturn(Mono.empty());
|
||||||
|
ViewResolver viewResolver = mock(ViewResolver.class);
|
||||||
|
given(viewResolver.resolveViewName(any(), any())).willReturn(Mono.just(view));
|
||||||
|
exceptionHandler.setViewResolvers(Collections.singletonList(viewResolver));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue