Remove @ControllerAdvice from BasicErrorController
Update the BasicErrorController so that it no longer needs to implement @ControllerAdvice or have an @ExceptionHandler method. A new ErrorAttributes interface is now used to obtain error details, the DefaultErrorAttributes implementation uses a HandlerExceptionResolver to obtain root exception details if the `javax.servlet.error.*` attributes are missing. This change also removes the need for the extract(...) method on ErrorController as classes such as WebRequestTraceFilter can now use the ErrorAttributes interface directly. See gh-839, gh-538 Fixes gh-843
This commit is contained in:
parent
5dd77a725c
commit
30ac768cbf
|
|
@ -34,7 +34,7 @@ import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.autoconfigure.web.ErrorController;
|
||||
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
|
||||
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
|
||||
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
|
||||
|
|
@ -125,12 +125,11 @@ public class EndpointWebMvcChildContextConfiguration {
|
|||
/*
|
||||
* The error controller is present but not mapped as an endpoint in this context
|
||||
* because of the DispatcherServlet having had it's HandlerMapping explicitly
|
||||
* disabled. So this tiny shim exposes the same feature but only for machine
|
||||
* endpoints.
|
||||
* disabled. So we expose the same feature but only for machine endpoints.
|
||||
*/
|
||||
@Bean
|
||||
public ManagementErrorEndpoint errorEndpoint(final ErrorController controller) {
|
||||
return new ManagementErrorEndpoint(this.errorPath, controller);
|
||||
public ManagementErrorEndpoint errorEndpoint(final ErrorAttributes errorAttributes) {
|
||||
return new ManagementErrorEndpoint(this.errorPath, errorAttributes);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import org.springframework.boot.actuate.trace.WebRequestTraceFilter;
|
|||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.web.BasicErrorController;
|
||||
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ public class TraceWebFilterAutoConfiguration {
|
|||
private TraceRepository traceRepository;
|
||||
|
||||
@Autowired(required = false)
|
||||
private BasicErrorController errorController;
|
||||
private ErrorAttributes errorAttributes;
|
||||
|
||||
@Value("${management.dump_requests:false}")
|
||||
private boolean dumpRequests;
|
||||
|
|
@ -54,8 +54,8 @@ public class TraceWebFilterAutoConfiguration {
|
|||
public WebRequestTraceFilter webRequestLoggingFilter(BeanFactory beanFactory) {
|
||||
WebRequestTraceFilter filter = new WebRequestTraceFilter(this.traceRepository);
|
||||
filter.setDumpRequests(this.dumpRequests);
|
||||
if (this.errorController != null) {
|
||||
filter.setErrorController(this.errorController);
|
||||
if (this.errorAttributes != null) {
|
||||
filter.setErrorAttributes(this.errorAttributes);
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ package org.springframework.boot.actuate.endpoint.mvc;
|
|||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
|
||||
import org.springframework.boot.autoconfigure.web.ErrorController;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
|
||||
/**
|
||||
|
|
@ -37,21 +37,21 @@ import org.springframework.web.context.request.RequestContextHolder;
|
|||
@ConfigurationProperties(prefix = "error")
|
||||
public class ManagementErrorEndpoint implements MvcEndpoint {
|
||||
|
||||
private final ErrorController controller;
|
||||
private final ErrorAttributes errorAttributes;
|
||||
|
||||
private final String path;
|
||||
|
||||
public ManagementErrorEndpoint(String path, ErrorController controller) {
|
||||
Assert.notNull(controller, "Controller must not be null");
|
||||
public ManagementErrorEndpoint(String path, ErrorAttributes errorAttributes) {
|
||||
Assert.notNull(errorAttributes, "ErrorAttributes must not be null");
|
||||
this.path = path;
|
||||
this.controller = controller;
|
||||
this.errorAttributes = errorAttributes;
|
||||
}
|
||||
|
||||
@RequestMapping
|
||||
@ResponseBody
|
||||
public Map<String, Object> invoke() {
|
||||
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
|
||||
return this.controller.extract(attributes, false, true);
|
||||
return this.errorAttributes.getErrorAttributes(
|
||||
RequestContextHolder.currentRequestAttributes(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -34,8 +34,9 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.boot.autoconfigure.web.BasicErrorController;
|
||||
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
|
|
@ -58,7 +59,7 @@ public class WebRequestTraceFilter implements Filter, Ordered {
|
|||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private BasicErrorController errorController;
|
||||
private ErrorAttributes errorAttributes;
|
||||
|
||||
/**
|
||||
* @param traceRepository
|
||||
|
|
@ -156,13 +157,13 @@ public class WebRequestTraceFilter implements Filter, Ordered {
|
|||
trace.put("method", request.getMethod());
|
||||
trace.put("path", request.getRequestURI());
|
||||
trace.put("headers", allHeaders);
|
||||
Throwable error = (Throwable) request
|
||||
Throwable exception = (Throwable) request
|
||||
.getAttribute("javax.servlet.error.exception");
|
||||
if (error != null) {
|
||||
if (this.errorController != null) {
|
||||
trace.put("error", this.errorController.extract(
|
||||
new ServletRequestAttributes(request), true, false));
|
||||
}
|
||||
if (exception != null && this.errorAttributes != null) {
|
||||
RequestAttributes requestAttributes = new ServletRequestAttributes(request);
|
||||
Map<String, Object> error = this.errorAttributes.getErrorAttributes(
|
||||
requestAttributes, true);
|
||||
trace.put("error", error);
|
||||
}
|
||||
return trace;
|
||||
}
|
||||
|
|
@ -175,8 +176,8 @@ public class WebRequestTraceFilter implements Filter, Ordered {
|
|||
public void destroy() {
|
||||
}
|
||||
|
||||
public void setErrorController(BasicErrorController errorController) {
|
||||
this.errorController = errorController;
|
||||
public void setErrorAttributes(ErrorAttributes errorAttributes) {
|
||||
this.errorAttributes = errorAttributes;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -19,7 +19,7 @@ package org.springframework.boot.actuate.trace;
|
|||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.autoconfigure.web.BasicErrorController;
|
||||
import org.springframework.boot.autoconfigure.web.DefaulErrorAttributes;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ public class WebRequestTraceFilterTests {
|
|||
|
||||
@Test
|
||||
public void filterHasError() {
|
||||
this.filter.setErrorController(new BasicErrorController());
|
||||
this.filter.setErrorAttributes(new DefaulErrorAttributes());
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.setStatus(500);
|
||||
|
|
|
|||
|
|
@ -20,9 +20,6 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
|
|
@ -35,9 +32,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||
|
|
@ -51,13 +46,10 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
|
|||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
||||
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.header.writers.HstsHeaderWriter;
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.servlet.support.RequestDataValueProcessor;
|
||||
|
||||
/**
|
||||
|
|
@ -109,18 +101,6 @@ public class SpringBootWebSecurityConfiguration {
|
|||
return new IgnoredPathsWebSecurityConfigurerAdapter();
|
||||
}
|
||||
|
||||
@ControllerAdvice
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 10)
|
||||
protected static class SecurityExceptionRethrowingAdvice {
|
||||
|
||||
@ExceptionHandler({ AccessDeniedException.class, AuthenticationException.class })
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response,
|
||||
Exception e) throws Exception {
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void configureHeaders(HeadersConfigurer<?> configurer,
|
||||
SecurityProperties.Headers headers) throws Exception {
|
||||
if (headers.getHsts() != Headers.HSTS.none) {
|
||||
|
|
|
|||
|
|
@ -16,188 +16,89 @@
|
|||
|
||||
package org.springframework.boot.autoconfigure.web;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||
|
||||
/**
|
||||
* Basic global error {@link Controller}, rendering servlet container error codes and
|
||||
* messages where available. More specific errors can be handled either using Spring MVC
|
||||
* abstractions (e.g. {@code @ExceptionHandler}) or by adding servlet
|
||||
* {@link AbstractEmbeddedServletContainerFactory#setErrorPages(java.util.Set) container
|
||||
* error pages}.
|
||||
* Basic global error {@link Controller}, rendering {@link ErrorAttributes}. More specific
|
||||
* errors can be handled either using Spring MVC abstractions (e.g.
|
||||
* {@code @ExceptionHandler}) or by adding servlet
|
||||
* {@link AbstractEmbeddedServletContainerFactory#setErrorPages container error pages}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
* @see ErrorAttributes
|
||||
*/
|
||||
@Controller
|
||||
@ControllerAdvice
|
||||
@Order(0)
|
||||
public class BasicErrorController implements ErrorController {
|
||||
|
||||
private static final String ERROR_KEY = "error";
|
||||
|
||||
private final Log logger = LogFactory.getLog(BasicErrorController.class);
|
||||
|
||||
private DefaultHandlerExceptionResolver resolver = new DefaultHandlerExceptionResolver();
|
||||
|
||||
private ResponseStatusExceptionResolver statuses = new ResponseStatusExceptionResolver();
|
||||
|
||||
@Value("${error.path:/error}")
|
||||
private String errorPath;
|
||||
|
||||
private final ErrorAttributes errorAttributes;
|
||||
|
||||
public BasicErrorController(ErrorAttributes errorAttributes) {
|
||||
Assert.notNull(errorAttributes, "ErrorAttributes must not be null");
|
||||
this.errorAttributes = errorAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorPath() {
|
||||
return this.errorPath;
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response,
|
||||
Exception e) throws Exception {
|
||||
if (this.statuses.resolveException(request, response, null, e) == null) {
|
||||
this.resolver.resolveException(request, response, null, e);
|
||||
}
|
||||
if (response.getStatus() == HttpServletResponse.SC_OK) {
|
||||
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
// There's only one exception so it's easier for the error controller to identify
|
||||
// it this way...
|
||||
request.setAttribute(ErrorController.class.getName(), e);
|
||||
if (e instanceof BindException) {
|
||||
// ... but other error pages might be looking for it here as well
|
||||
request.setAttribute(
|
||||
BindingResult.MODEL_KEY_PREFIX + ((BindException) e).getObjectName(),
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(value = "${error.path:/error}", produces = "text/html")
|
||||
public ModelAndView errorHtml(HttpServletRequest request) {
|
||||
Map<String, Object> map = extract(new ServletRequestAttributes(request), false,
|
||||
false);
|
||||
return new ModelAndView(ERROR_KEY, map);
|
||||
return new ModelAndView("error", getErrorAttributes(request, false));
|
||||
}
|
||||
|
||||
@RequestMapping(value = "${error.path:/error}")
|
||||
@ResponseBody
|
||||
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
|
||||
ServletRequestAttributes attributes = new ServletRequestAttributes(request);
|
||||
String trace = request.getParameter("trace");
|
||||
Map<String, Object> extracted = extract(attributes,
|
||||
trace != null && !"false".equals(trace.toLowerCase()), true);
|
||||
HttpStatus statusCode = getStatus((Integer) extracted.get("status"));
|
||||
return new ResponseEntity<Map<String, Object>>(extracted, statusCode);
|
||||
Map<String, Object> body = getErrorAttributes(request, getTraceParameter(request));
|
||||
HttpStatus status = getStatus(request);
|
||||
return new ResponseEntity<Map<String, Object>>(body, status);
|
||||
}
|
||||
|
||||
private HttpStatus getStatus(Integer value) {
|
||||
try {
|
||||
return HttpStatus.valueOf(value);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
private boolean getTraceParameter(HttpServletRequest request) {
|
||||
String parameter = request.getParameter("trace");
|
||||
if (parameter == null) {
|
||||
return false;
|
||||
}
|
||||
return !"false".equals(parameter.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> extract(RequestAttributes attributes, boolean trace,
|
||||
boolean log) {
|
||||
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
map.put("timestamp", new Date());
|
||||
try {
|
||||
Throwable error = (Throwable) attributes.getAttribute(
|
||||
ErrorController.class.getName(), RequestAttributes.SCOPE_REQUEST);
|
||||
Object obj = attributes.getAttribute("javax.servlet.error.status_code",
|
||||
RequestAttributes.SCOPE_REQUEST);
|
||||
int status = 999;
|
||||
if (obj != null) {
|
||||
status = (Integer) obj;
|
||||
map.put(ERROR_KEY, HttpStatus.valueOf(status).getReasonPhrase());
|
||||
}
|
||||
else {
|
||||
map.put(ERROR_KEY, "None");
|
||||
}
|
||||
map.put("status", status);
|
||||
if (error == null) {
|
||||
error = (Throwable) attributes.getAttribute(
|
||||
"javax.servlet.error.exception", RequestAttributes.SCOPE_REQUEST);
|
||||
}
|
||||
if (error != null) {
|
||||
while (error instanceof ServletException && error.getCause() != null) {
|
||||
error = ((ServletException) error).getCause();
|
||||
}
|
||||
map.put("exception", error.getClass().getName());
|
||||
addMessage(map, error);
|
||||
if (trace) {
|
||||
StringWriter stackTrace = new StringWriter();
|
||||
error.printStackTrace(new PrintWriter(stackTrace));
|
||||
stackTrace.flush();
|
||||
map.put("trace", stackTrace.toString());
|
||||
}
|
||||
if (log) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Object message = attributes.getAttribute("javax.servlet.error.message",
|
||||
RequestAttributes.SCOPE_REQUEST);
|
||||
map.put("message", message == null ? "No message available" : message);
|
||||
}
|
||||
String path = (String) attributes.getAttribute(
|
||||
"javax.servlet.error.request_uri", RequestAttributes.SCOPE_REQUEST);
|
||||
map.put("path", path == null ? "No path available" : path);
|
||||
return map;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
map.put(ERROR_KEY, ex.getClass().getName());
|
||||
map.put("message", ex.getMessage());
|
||||
if (log) {
|
||||
this.logger.error(ex);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
private Map<String, Object> getErrorAttributes(HttpServletRequest request,
|
||||
boolean includeStackTrace) {
|
||||
RequestAttributes requestAttributes = new ServletRequestAttributes(request);
|
||||
return this.errorAttributes.getErrorAttributes(requestAttributes,
|
||||
includeStackTrace);
|
||||
}
|
||||
|
||||
protected void addMessage(Map<String, Object> map, Throwable error) {
|
||||
if (error instanceof BindingResult) {
|
||||
BindingResult result = (BindingResult) error;
|
||||
if (result.getErrorCount() > 0) {
|
||||
map.put("errors", result.getAllErrors());
|
||||
map.put("message",
|
||||
"Validation failed for object='" + result.getObjectName()
|
||||
+ "'. Error count: " + result.getErrorCount());
|
||||
private HttpStatus getStatus(HttpServletRequest request) {
|
||||
Integer statusCode = (Integer) request
|
||||
.getAttribute("javax.servlet.error.status_code");
|
||||
if (statusCode != null) {
|
||||
try {
|
||||
return HttpStatus.valueOf(statusCode);
|
||||
}
|
||||
else {
|
||||
map.put("message", "No errors");
|
||||
catch (Exception ex) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
map.put("message", error.getMessage());
|
||||
}
|
||||
return HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.web;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ErrorAttributes}. Provides the following attributes
|
||||
* when possible:
|
||||
* <ul>
|
||||
* <li>timestamp - The time that the errors were extracted</li>
|
||||
* <li>status - The status code</li>
|
||||
* <li>error - The error reason</li>
|
||||
* <li>exception - The class name of the root exception</li>
|
||||
* <li>message - The exception message</li>
|
||||
* <li>errors - Any {@link ObjectError}s from a {@link BindingResult} exception
|
||||
* <li>trace - The exception stack trace</li>
|
||||
* <li>path - The URL path when the exception was raised</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @since 1.1.0
|
||||
* @see ErrorAttributes
|
||||
*/
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public class DefaulErrorAttributes implements ErrorAttributes, HandlerExceptionResolver,
|
||||
Ordered {
|
||||
|
||||
private static final String ERROR_ATTRIBUTE = DefaulErrorAttributes.class.getName()
|
||||
+ ".ERROR";
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.HIGHEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelAndView resolveException(HttpServletRequest request,
|
||||
HttpServletResponse response, Object handler, Exception ex) {
|
||||
storeErrorAttributes(request, ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
private void storeErrorAttributes(HttpServletRequest request, Exception ex) {
|
||||
request.setAttribute(ERROR_ATTRIBUTE, ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
|
||||
boolean includeStackTrace) {
|
||||
Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();
|
||||
errorAttributes.put("timestamp", new Date());
|
||||
addStatus(errorAttributes, requestAttributes);
|
||||
addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
|
||||
addPath(errorAttributes, requestAttributes);
|
||||
return errorAttributes;
|
||||
}
|
||||
|
||||
private void addStatus(Map<String, Object> errorAttributes,
|
||||
RequestAttributes requestAttributes) {
|
||||
Integer status = getAttribute(requestAttributes,
|
||||
"javax.servlet.error.status_code");
|
||||
if (status == null) {
|
||||
errorAttributes.put("status", 999);
|
||||
errorAttributes.put("error", "None");
|
||||
return;
|
||||
}
|
||||
errorAttributes.put("status", status);
|
||||
try {
|
||||
errorAttributes.put("error", HttpStatus.valueOf(status).getReasonPhrase());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Unable to obtain a reason
|
||||
errorAttributes.put("error", "Http Status " + status);
|
||||
}
|
||||
}
|
||||
|
||||
private void addErrorDetails(Map<String, Object> errorAttributes,
|
||||
RequestAttributes requestAttributes, boolean includeStackTrace) {
|
||||
Throwable error = getError(requestAttributes);
|
||||
if (error != null) {
|
||||
while (error instanceof ServletException && error.getCause() != null) {
|
||||
error = ((ServletException) error).getCause();
|
||||
}
|
||||
errorAttributes.put("exception", error.getClass().getName());
|
||||
addErrorMessage(errorAttributes, error);
|
||||
if (includeStackTrace) {
|
||||
addStackTrace(errorAttributes, error);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Object message = getAttribute(requestAttributes,
|
||||
"javax.servlet.error.message");
|
||||
errorAttributes.put("message", message == null ? "No message available"
|
||||
: message);
|
||||
}
|
||||
}
|
||||
|
||||
private void addErrorMessage(Map<String, Object> errorAttributes, Throwable error) {
|
||||
if (!(error instanceof BindingResult)) {
|
||||
errorAttributes.put("message", error.getMessage());
|
||||
return;
|
||||
}
|
||||
BindingResult result = (BindingResult) error;
|
||||
if (result.getErrorCount() > 0) {
|
||||
errorAttributes.put("errors", result.getAllErrors());
|
||||
errorAttributes.put("message",
|
||||
"Validation failed for object='" + result.getObjectName()
|
||||
+ "'. Error count: " + result.getErrorCount());
|
||||
}
|
||||
else {
|
||||
errorAttributes.put("message", "No errors");
|
||||
}
|
||||
}
|
||||
|
||||
private void addStackTrace(Map<String, Object> errorAttributes, Throwable error) {
|
||||
StringWriter stackTrace = new StringWriter();
|
||||
error.printStackTrace(new PrintWriter(stackTrace));
|
||||
stackTrace.flush();
|
||||
errorAttributes.put("trace", stackTrace.toString());
|
||||
}
|
||||
|
||||
private void addPath(Map<String, Object> errorAttributes,
|
||||
RequestAttributes requestAttributes) {
|
||||
String path = getAttribute(requestAttributes, "javax.servlet.error.request_uri");
|
||||
if (path != null) {
|
||||
errorAttributes.put("path", path);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Throwable getError(RequestAttributes requestAttributes) {
|
||||
Exception exception = getAttribute(requestAttributes, ERROR_ATTRIBUTE);
|
||||
if (exception == null) {
|
||||
exception = getAttribute(requestAttributes, "javax.servlet.error.exception");
|
||||
}
|
||||
return exception;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T getAttribute(RequestAttributes requestAttributes, String name) {
|
||||
return (T) requestAttributes.getAttribute(name, RequestAttributes.SCOPE_REQUEST);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.web;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* Provides access to error attributes which can be logged or presented to the user.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.1.0
|
||||
* @see DefaulErrorAttributes
|
||||
*/
|
||||
public interface ErrorAttributes {
|
||||
|
||||
/**
|
||||
* Returns a {@link Map} of the error attributes. The map can be used as the model of
|
||||
* an error page {@link ModelAndView}, or returned as a {@link ResponseBody}.
|
||||
* @param requestAttributes the source request attributes
|
||||
* @param includeStackTrace if stack trace elements should be included
|
||||
* @return a map of error attributes
|
||||
*/
|
||||
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
|
||||
boolean includeStackTrace);
|
||||
|
||||
/**
|
||||
* Return the underlying cause of the error or {@code null} if the error cannot be
|
||||
* extracted.
|
||||
* @param requestAttributes the source request attributes
|
||||
* @return the {@link Exception} that caused the error or {@code null}
|
||||
*/
|
||||
public Throwable getError(RequestAttributes requestAttributes);
|
||||
|
||||
}
|
||||
|
|
@ -16,14 +16,11 @@
|
|||
|
||||
package org.springframework.boot.autoconfigure.web;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
|
||||
/**
|
||||
* Marker interface used to indicate that a {@link Controller @Controller} is used to
|
||||
* render errors.
|
||||
* render errors. Primarily used to know the error paths that will not need to be secured.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
|
|
@ -34,14 +31,4 @@ public interface ErrorController {
|
|||
*/
|
||||
public String getErrorPath();
|
||||
|
||||
/**
|
||||
* Extract a useful model of the error from the request attributes.
|
||||
* @param attributes the request attributes
|
||||
* @param trace flag to indicate that stack trace information should be included
|
||||
* @param log flag to indicate that an error should be logged
|
||||
* @return a model containing error messages and codes etc.
|
||||
*/
|
||||
public Map<String, Object> extract(RequestAttributes attributes, boolean trace,
|
||||
boolean log);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,10 +72,16 @@ public class ErrorMvcAutoConfiguration implements EmbeddedServletContainerCustom
|
|||
@Value("${error.path:/error}")
|
||||
private String errorPath = "/error";
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
|
||||
public DefaulErrorAttributes errorAttributes() {
|
||||
return new DefaulErrorAttributes();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
|
||||
public BasicErrorController basicErrorController() {
|
||||
return new BasicErrorController();
|
||||
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
|
||||
return new BasicErrorController(errorAttributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -136,6 +142,9 @@ public class ErrorMvcAutoConfiguration implements EmbeddedServletContainerCustom
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple {@link View} implementation that resolves variables as SpEL expressions.
|
||||
*/
|
||||
private static class SpelView implements View {
|
||||
|
||||
private final String template;
|
||||
|
|
@ -156,8 +165,13 @@ public class ErrorMvcAutoConfiguration implements EmbeddedServletContainerCustom
|
|||
@Override
|
||||
public String resolvePlaceholder(String name) {
|
||||
Expression expression = SpelView.this.parser.parseExpression(name);
|
||||
Object value = expression.getValue(SpelView.this.context);
|
||||
return value == null ? null : value.toString();
|
||||
try {
|
||||
Object value = expression.getValue(SpelView.this.context);
|
||||
return (value == null ? null : value.toString());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -26,6 +26,7 @@ import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration
|
|||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
|
@ -37,9 +38,12 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for {@link BasicErrorController} using {@link MockMvc} but not
|
||||
* {@link SpringJUnit4ClassRunner}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class BasicErrorControllerSpecialIntegrationTests {
|
||||
public class BasicErrorControllerDirectMockMvcTests {
|
||||
|
||||
private ConfigurableWebApplicationContext wac;
|
||||
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.web;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.boot.test.TestRestTemplate;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link BasicErrorController} using {@link IntegrationTest} that hit a real
|
||||
* HTTP server.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = BasicErrorControllerMockMvcTests.TestConfiguration.class)
|
||||
@WebAppConfiguration
|
||||
@IntegrationTest("server.port=0")
|
||||
public class BasicErrorControllerIntegrationTest {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port;
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testErrorForMachineClient() throws Exception {
|
||||
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:" + this.port, Map.class);
|
||||
assertThat(entity.getBody().toString(), endsWith("status=500, "
|
||||
+ "error=Internal Server Error, "
|
||||
+ "exception=java.lang.IllegalStateException, " + "message=Expected!, "
|
||||
+ "path=/}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testBindingExceptionForMachineClient() throws Exception {
|
||||
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:" + this.port + "/bind", Map.class);
|
||||
String resp = entity.getBody().toString();
|
||||
assertThat(resp, containsString("Error count: 1"));
|
||||
assertThat(resp, containsString("errors=[{codes="));
|
||||
assertThat(resp, containsString("org.springframework.validation.BindException"));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -32,7 +32,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
|||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.BasicErrorControllerIntegrationTests.TestConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.BasicErrorControllerMockMvcTests.TestConfiguration;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
|
@ -59,13 +59,16 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for {@link BasicErrorController} using {@link MockMvc} and
|
||||
* {@link SpringJUnit4ClassRunner}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@SpringApplicationConfiguration(classes = TestConfiguration.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@WebAppConfiguration
|
||||
@DirtiesContext
|
||||
public class BasicErrorControllerIntegrationTests {
|
||||
public class BasicErrorControllerMockMvcTests {
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext wac;
|
||||
|
|
@ -85,16 +88,6 @@ public class BasicErrorControllerIntegrationTests {
|
|||
assertTrue("Wrong content: " + content, content.contains("999"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErrorForMachineClient() throws Exception {
|
||||
MvcResult result = this.mockMvc.perform(get("/"))
|
||||
.andExpect(status().is5xxServerError()).andReturn();
|
||||
MvcResult response = this.mockMvc.perform(new ErrorDispatcher(result, "/error"))
|
||||
.andReturn();
|
||||
String content = response.getResponse().getContentAsString();
|
||||
assertTrue("Wrong content: " + content, content.contains("Expected!"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErrorWithResponseStatus() throws Exception {
|
||||
MvcResult result = this.mockMvc.perform(get("/bang"))
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.web;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.MapBindingResult;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.Matchers.sameInstance;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaulErrorAttributes}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class DefaultErrorAttributesTests {
|
||||
|
||||
private DefaulErrorAttributes errorAttributes = new DefaulErrorAttributes();
|
||||
|
||||
private MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
|
||||
private RequestAttributes requestAttributes = new ServletRequestAttributes(
|
||||
this.request);
|
||||
|
||||
@Test
|
||||
public void includeTimeStamp() throws Exception {
|
||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(
|
||||
this.requestAttributes, false);
|
||||
assertThat(attributes.get("timestamp"), instanceOf(Date.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specificStatusCode() throws Exception {
|
||||
this.request.setAttribute("javax.servlet.error.status_code", 404);
|
||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(
|
||||
this.requestAttributes, false);
|
||||
assertThat(attributes.get("error"),
|
||||
equalTo((Object) HttpStatus.NOT_FOUND.getReasonPhrase()));
|
||||
assertThat(attributes.get("status"), equalTo((Object) 404));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingStatusCode() throws Exception {
|
||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(
|
||||
this.requestAttributes, false);
|
||||
assertThat(attributes.get("error"), equalTo((Object) "None"));
|
||||
assertThat(attributes.get("status"), equalTo((Object) 999));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mvcError() throws Exception {
|
||||
RuntimeException ex = new RuntimeException("Test");
|
||||
ModelAndView modelAndView = this.errorAttributes.resolveException(this.request,
|
||||
null, null, ex);
|
||||
this.request.setAttribute("javax.servlet.error.exception", new RuntimeException(
|
||||
"Ignored"));
|
||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(
|
||||
this.requestAttributes, false);
|
||||
assertThat(this.errorAttributes.getError(this.requestAttributes),
|
||||
sameInstance((Object) ex));
|
||||
assertThat(modelAndView, nullValue());
|
||||
assertThat(attributes.get("exception"),
|
||||
equalTo((Object) RuntimeException.class.getName()));
|
||||
assertThat(attributes.get("message"), equalTo((Object) "Test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void servletError() throws Exception {
|
||||
RuntimeException ex = new RuntimeException("Test");
|
||||
this.request.setAttribute("javax.servlet.error.exception", ex);
|
||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(
|
||||
this.requestAttributes, false);
|
||||
assertThat(this.errorAttributes.getError(this.requestAttributes),
|
||||
sameInstance((Object) ex));
|
||||
assertThat(attributes.get("exception"),
|
||||
equalTo((Object) RuntimeException.class.getName()));
|
||||
assertThat(attributes.get("message"), equalTo((Object) "Test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void servletMessage() throws Exception {
|
||||
this.request.setAttribute("javax.servlet.error.message", "Test");
|
||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(
|
||||
this.requestAttributes, false);
|
||||
assertThat(attributes.get("exception"), nullValue());
|
||||
assertThat(attributes.get("message"), equalTo((Object) "Test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unwrapServletException() throws Exception {
|
||||
RuntimeException ex = new RuntimeException("Test");
|
||||
ServletException wrapped = new ServletException(new ServletException(ex));
|
||||
this.request.setAttribute("javax.servlet.error.exception", wrapped);
|
||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(
|
||||
this.requestAttributes, false);
|
||||
assertThat(this.errorAttributes.getError(this.requestAttributes),
|
||||
sameInstance((Object) wrapped));
|
||||
assertThat(attributes.get("exception"),
|
||||
equalTo((Object) RuntimeException.class.getName()));
|
||||
assertThat(attributes.get("message"), equalTo((Object) "Test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractBindingResultErrors() throws Exception {
|
||||
BindingResult bindingResult = new MapBindingResult(Collections.singletonMap("a",
|
||||
"b"), "objectName");
|
||||
bindingResult.addError(new ObjectError("c", "d"));
|
||||
BindException ex = new BindException(bindingResult);
|
||||
this.request.setAttribute("javax.servlet.error.exception", ex);
|
||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(
|
||||
this.requestAttributes, false);
|
||||
assertThat(attributes.get("message"), equalTo((Object) ("Validation failed for "
|
||||
+ "object='objectName'. Error count: 1")));
|
||||
assertThat(attributes.get("errors"),
|
||||
equalTo((Object) bindingResult.getAllErrors()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void trace() throws Exception {
|
||||
RuntimeException ex = new RuntimeException("Test");
|
||||
this.request.setAttribute("javax.servlet.error.exception", ex);
|
||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(
|
||||
this.requestAttributes, true);
|
||||
assertThat(attributes.get("trace").toString(), startsWith("java.lang"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noTrace() throws Exception {
|
||||
RuntimeException ex = new RuntimeException("Test");
|
||||
this.request.setAttribute("javax.servlet.error.exception", ex);
|
||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(
|
||||
this.requestAttributes, false);
|
||||
assertThat(attributes.get("trace"), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void path() throws Exception {
|
||||
this.request.setAttribute("javax.servlet.error.request_uri", "path");
|
||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(
|
||||
this.requestAttributes, false);
|
||||
assertThat(attributes.get("path"), equalTo((Object) "path"));
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue