Commit 4b4dc28a authored by Madhura Bhave's avatar Madhura Bhave

Support non-standard error codes with AbstractErrorWebExceptionHandler

Fixes gh-16691
parent a695e062
...@@ -62,6 +62,7 @@ public abstract class AbstractErrorWebExceptionHandler implements ErrorWebExcept ...@@ -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 ...@@ -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 @@ ...@@ -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 ...@@ -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 ...@@ -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 @@ ...@@ -16,17 +16,37 @@
package org.springframework.boot.autoconfigure.web.reactive.error; package org.springframework.boot.autoconfigure.web.reactive.error;
import java.util.Collections;
import java.util.Map;
import org.junit.jupiter.api.Test; 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 { ...@@ -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));
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment