spring boot之Whitelabel-Error-Page实现

spring boot之Whitelabel-Error-Page实现

问题描述

当访问boot服务时,如果服务器异常或者404,服务器会自动跳转到Whitelabel-Error-Page

探索

都知道boot自动装配的信息都在spring-boot-autoconfigure.jar中的spring.factories中,找到boot中ErrorMvcAutoConfiguration:

//...
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\

那我们看下ErrorMvcAutoConfiguration:


@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
	return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
}

这个就有意思了:

public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered {
  ...
}

继承了HandlerExceptionResolver,意味着在spring mvc中处理异常的时候会自动被注入,这样就和spring mvc挂钩了

@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
  return new BasicErrorController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers);
}

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
  //omit....

	@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);
	}
}

这个主要是/error页面请求的显示,如果是浏览器,则返回页面,如果是其他的,就返回json格式数据

@Bean
public ErrorPageCustomizer errorPageCustomizer() {
  return new ErrorPageCustomizer(this.serverProperties, this.dispatcherServletPath);
}

private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {

	private final ServerProperties properties;

	private final DispatcherServletPath dispatcherServletPath;

	protected ErrorPageCustomizer(ServerProperties properties, DispatcherServletPath dispatcherServletPath) {
		this.properties = properties;
		this.dispatcherServletPath = dispatcherServletPath;
	}

	@Override
	public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
		ErrorPage errorPage = new ErrorPage(
				this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));
		errorPageRegistry.addErrorPages(errorPage);
	}

	@Override
	public int getOrder() {
		return 0;
	}

}

这个就比较重要了,向ErrorPageRegistry中注册了错误页面,即注册/error这个请求,那是在什么时候注册的呢?,在ErrorPageRegistrarBeanPostProcessor中:

public class ErrorPageRegistrarBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {

  //...
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if (bean instanceof ErrorPageRegistry) {
			postProcessBeforeInitialization((ErrorPageRegistry) bean);
		}
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	private void postProcessBeforeInitialization(ErrorPageRegistry registry) {
		for (ErrorPageRegistrar registrar : getRegistrars()) {
			registrar.registerErrorPages(registry);
		}
	}

}

再回到ErrorMvcAutoConfiguration中,WhitelabelErrorViewConfiguration大概就是配置WhiteLabel-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;
	}

	// If the user adds @EnableWebMvc then the bean name view resolver from
	// WebMvcAutoConfiguration disappears, so add it back in to avoid disappointment.
	@Bean
	@ConditionalOnMissingBean
	public BeanNameViewResolver beanNameViewResolver() {
		BeanNameViewResolver resolver = new BeanNameViewResolver();
		resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
		return resolver;
	}

}

看下@Conditional(ErrorTemplateMissingCondition.class)@Conditional代表括号内部条件成立:

//在找到任何匹配的错误页面模板时,就使用默认的错误页面,即WhiteLable error page
private static class ErrorTemplateMissingCondition extends SpringBootCondition {

		@Override
		public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
			ConditionMessage.Builder message = ConditionMessage.forCondition("ErrorTemplate Missing");
			TemplateAvailabilityProviders providers = new TemplateAvailabilityProviders(context.getClassLoader());
			TemplateAvailabilityProvider provider = providers.getProvider("error", context.getEnvironment(),
					context.getClassLoader(), context.getResourceLoader());
			if (provider != null) {
				return ConditionOutcome.noMatch(message.foundExactly("template from " + provider));
			}
			return ConditionOutcome.match(message.didNotFind("error template view").atAll());
		}

	}

对于DefaultErrorViewResolverConfiguration,这个主要是配置了http_code为4xx,5xx对应的错误页面:

@Configuration
static class DefaultErrorViewResolverConfiguration {

	private final ApplicationContext applicationContext;

	private final ResourceProperties resourceProperties;

	DefaultErrorViewResolverConfiguration(ApplicationContext applicationContext,
			ResourceProperties resourceProperties) {
		this.applicationContext = applicationContext;
		this.resourceProperties = resourceProperties;
	}

	@Bean
	@ConditionalOnBean(DispatcherServlet.class)
	@ConditionalOnMissingBean(ErrorViewResolver.class)
	public DefaultErrorViewResolver conventionErrorViewResolver() {
		return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties);
	}

}

DefaultErrorViewResolver中:

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;
}

即在classpath下建立error/目录下例如404.html,500.html就可以了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值