错误处理的自动配置
@Configuration
@ConditionalOnWebApplication ( type = Type. SERVLET)
@ConditionalOnClass ( { Servlet. class , DispatcherServlet. class } )
@AutoConfigureBefore ( WebMvcAutoConfiguration. class )
@EnableConfigurationProperties ( { ServerProperties. class , ResourceProperties. class , WebMvcProperties. class } )
public class ErrorMvcAutoConfiguration {
@Bean
@ConditionalOnMissingBean ( value = ErrorAttributes. class , search = SearchStrategy. CURRENT)
public DefaultErrorAttributes errorAttributes ( ) {
return new DefaultErrorAttributes ( this . serverProperties. getError ( ) . isIncludeException ( ) ) ;
}
@Bean
@ConditionalOnMissingBean ( value = ErrorController. class , search = SearchStrategy. CURRENT)
public BasicErrorController basicErrorController ( ErrorAttributes errorAttributes) {
return new BasicErrorController ( errorAttributes, this . serverProperties. getError ( ) , this . errorViewResolvers) ;
}
@Bean
public ErrorPageCustomizer errorPageCustomizer ( ) {
return new ErrorPageCustomizer ( this . serverProperties, this . dispatcherServletPath) ;
}
@Configuration
static class DefaultErrorViewResolverConfiguration {
@Bean
@ConditionalOnBean ( DispatcherServlet. class )
@ConditionalOnMissingBean ( ErrorViewResolver. class )
public DefaultErrorViewResolver conventionErrorViewResolver ( ) {
return new DefaultErrorViewResolver ( this . applicationContext, this . resourceProperties) ;
}
}
private static class ErrorPageCustomizer implements ErrorPageRegistrar , Ordered {
@Override
public void registerErrorPages ( ErrorPageRegistry errorPageRegistry) {
ErrorPage errorPage = new ErrorPage (
this . dispatcherServletPath. getRelativePath ( this . properties. getError ( ) . getPath ( ) ) ) ;
errorPageRegistry. addErrorPages ( errorPage) ;
}
}
@Configuration
@ConditionalOnProperty ( prefix = "server.error.whitelabel" , name = "enabled" , matchIfMissing = true )
@Conditional ( ErrorTemplateMissingCondition. class )
protected static class WhitelabelErrorViewConfiguration {
private final StaticView defaultErrorView = new StaticView ( ) ;
@Bean ( name = "error" )
@ConditionalOnMissingBean ( name = "error" )
public View defaultErrorView ( ) {
return this . defaultErrorView;
}
}
private static class StaticView implements View {
@Override
public void render ( Map< String, ? > model, HttpServletRequest request, HttpServletResponse response)
private static final Log logger = LogFactory. getLog ( StaticView. class ) ;
@Override
public void render ( Map< String, ? > model, HttpServletRequest request, HttpServletResponse response)
throws Exception {
if ( response. isCommitted ( ) ) {
String message = getMessage ( model) ;
logger. error ( message) ;
return ;
}
StringBuilder builder = new StringBuilder ( ) ;
Date timestamp = ( Date) model. get ( "timestamp" ) ;
Object message = model. get ( "message" ) ;
Object trace = model. get ( "trace" ) ;
if ( response. getContentType ( ) == null) {
response. setContentType ( getContentType ( ) ) ;
}
builder. append ( "<html><body><h1>Whitelabel Error Page</h1>" ) . append (
"<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>" )
. append ( "<div id='created'>" ) . append ( timestamp) . append ( "</div>" )
. append ( "<div>There was an unexpected error (type=" ) . append ( htmlEscape ( model. get ( "error" ) ) )
. append ( ", status=" ) . append ( htmlEscape ( model. get ( "status" ) ) ) . append ( ").</div>" ) ;
if ( message != null) {
builder. append ( "<div>" ) . append ( htmlEscape ( message) ) . append ( "</div>" ) ;
}
if ( trace != null) {
builder. append ( "<div style='white-space:pre-wrap;'>" ) . append ( htmlEscape ( trace) ) . append ( "</div>" ) ;
}
builder. append ( "</body></html>" ) ;
response. getWriter ( ) . append ( builder. toString ( ) ) ;
}
}
}
getPath()
源码:
public class ErrorProperties {
@Value ( "${error.path:/error}" )
private String path = "/error" ;
}
BasicErrorController//处理/error请求的
源码:
@Controller
@RequestMapping ( "${server.error.path:${error.path:/error}}" )
public class BasicErrorController extends AbstractErrorController {
@RequestMapping ( produces = MediaType. TEXT_HTML_VALUE)
public ModelAndView errorHtml ( HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus ( request) ;
Map< String, Object> model = Collections
. unmodifiableMap ( getErrorAttributes ( request, isIncludeStackTrace ( request, MediaType. TEXT_HTML) ) ) ;
response. setStatus ( status. value ( ) ) ;
ModelAndView modelAndView = resolveErrorView ( request, response, status, model) ;
return ( modelAndView != null) ? modelAndView : new ModelAndView ( "error" , model) ;
}
@RequestMapping
public ResponseEntity< Map< String, Object> > error ( HttpServletRequest request) {
HttpStatus status = getStatus ( request) ;
if ( status == HttpStatus. NO_CONTENT) {
return new ResponseEntity < Map< String, Object> > ( status) ;
}
Map< String, Object> body = getErrorAttributes ( request, isIncludeStackTrace ( request, MediaType. ALL) ) ;
return new ResponseEntity < > ( body, status) ;
}
}
TEXT_HTML_VALUE
源码:
public static final String TEXT_HTML_VALUE = "text/html" ;
getStatus
源码:
public abstract class AbstractErrorController implements ErrorController {
protected HttpStatus getStatus ( HttpServletRequest request) {
Integer statusCode = ( Integer) request. getAttribute ( "javax.servlet.error.status_code" ) ;
if ( statusCode == null) {
return HttpStatus. INTERNAL_SERVER_ERROR;
}
try {
return HttpStatus. valueOf ( statusCode) ;
}
catch ( Exception ex) {
return HttpStatus. INTERNAL_SERVER_ERROR;
}
}
}
getErrorAttributes
源码:
protected Map< String, Object> getErrorAttributes ( HttpServletRequest request, boolean includeStackTrace) {
WebRequest webRequest = new ServletWebRequest ( request) ;
return this . errorAttributes. getErrorAttributes ( webRequest, includeStackTrace) ;
}
errorAttributes
源码:
public interface ErrorAttributes {
}
ErrorAttributes
实现:
@Order ( Ordered. HIGHEST_PRECEDENCE)
public class DefaultErrorAttributes implements ErrorAttributes , HandlerExceptionResolver, Ordered {
@Override
public Map< String, Object> getErrorAttributes ( WebRequest webRequest, boolean includeStackTrace) {
Map< String, Object> errorAttributes = new LinkedHashMap < > ( ) ;
errorAttributes. put ( "timestamp" , new Date ( ) ) ;
addStatus ( errorAttributes, webRequest) ;
addErrorDetails ( errorAttributes, webRequest, includeStackTrace) ;
addPath ( errorAttributes, webRequest) ;
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) {
errorAttributes. put ( "error" , "Http Status " + status) ;
}
}
private void addErrorDetails ( Map< String, Object> errorAttributes, WebRequest webRequest,
boolean includeStackTrace) {
Throwable error = getError ( webRequest) ;
if ( error != null) {
while ( error instanceof ServletException && error. getCause ( ) != null) {
error = error. getCause ( ) ;
}
if ( this . includeException) {
errorAttributes. put ( "exception" , error. getClass ( ) . getName ( ) ) ;
}
addErrorMessage ( errorAttributes, error) ;
if ( includeStackTrace) {
addStackTrace ( errorAttributes, error) ;
}
}
Object message = getAttribute ( webRequest, "javax.servlet.error.message" ) ;
if ( ( ! StringUtils. isEmpty ( message) || errorAttributes. get ( "message" ) == null)
&& ! ( error instanceof BindingResult ) ) {
errorAttributes. put ( "message" , StringUtils. isEmpty ( message) ? "No message available" : message) ;
}
}
private void addErrorMessage ( Map< String, Object> errorAttributes, Throwable error) {
BindingResult result = extractBindingResult ( error) ;
if ( result == null) {
errorAttributes. put ( "message" , error. getMessage ( ) ) ;
return ;
}
if ( result. hasErrors ( ) ) {
errorAttributes. put ( "errors" , result. getAllErrors ( ) ) ;
errorAttributes. put ( "message" , "Validation failed for object='" + result. getObjectName ( )
+ "'. Error count: " + result. getErrorCount ( ) ) ;
}
else {
errorAttributes. put ( "message" , "No errors" ) ;
}
}
}
resolveErrorView
源码:
public abstract class AbstractErrorController implements ErrorController {
protected ModelAndView resolveErrorView ( HttpServletRequest request, HttpServletResponse response, HttpStatus status,
Map< String, Object> model) {
for ( ErrorViewResolver resolver : this . errorViewResolvers) {
ModelAndView modelAndView = resolver. resolveErrorView ( request, status, model) ;
if ( modelAndView != null) {
return modelAndView;
}
}
return null;
}
}
DefaultErrorViewResolver//帮我们在页面共享信息
源码:
public class DefaultErrorViewResolver implements ErrorViewResolver , Ordered {
static {
Map< Series, String> views = new EnumMap < > ( Series. class ) ;
views. put ( Series. CLIENT_ERROR, "4xx" ) ;
views. put ( Series. SERVER_ERROR, "5xx" ) ;
SERIES_VIEWS = Collections. unmodifiableMap ( views) ;
}
@Override
public ModelAndView resolveErrorView ( HttpServletRequest request, HttpStatus status, Map< String, Object> model) {
ModelAndView modelAndView = resolve ( String. valueOf ( status. value ( ) ) , model) ;
if ( modelAndView == null && SERIES_VIEWS. containsKey ( status. series ( ) ) ) {
modelAndView = resolve ( SERIES_VIEWS. get ( status. series ( ) ) , model) ;
}
return modelAndView;
}
private ModelAndView resolve ( String viewName, Map< String, Object> model) {
String errorViewName = "error/" + viewName;
TemplateAvailabilityProvider provider = this . templateAvailabilityProviders. getProvider ( errorViewName,
this . applicationContext) ;
if ( provider != null) {
return new ModelAndView ( errorViewName, model) ;
}
return resolveResource ( errorViewName, model) ;
}
private ModelAndView resolveResource ( String viewName, Map< String, Object> model) {
for ( String location : this . resourceProperties. getStaticLocations ( ) ) {
try {
Resource resource = this . applicationContext. getResource ( location) ;
resource = resource. createRelative ( viewName + ".html" ) ;
if ( resource. exists ( ) ) {
return new ModelAndView ( new HtmlResourceView ( resource) , model) ;
}
}
catch ( Exception ex) {
}
}
return null;
}
}