出现白页的原因:
请求进入org.springframework.web.servlet.DispatcherServlet#doDispatch方法中:
#通过请求查询可供处理的handler
mappedHandler = getHandler(processedRequest);
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
#org.springframework.web.servlet.resource.ResourceHttpRequestHandler
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// For very general mappings (e.g. "/") we need to check 404 first 找不到资源返回
Resource resource = getResource(request);
if (resource == null) {
logger.debug("Resource not found");
# 找不到资源返回设置设置状态码为HttpServletResponse.SC_NOT_FOUND(HttpServletResponse.SC_NOT_FOUND代表的值为404)
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
#javax.servlet.http.HttpServletResponse#SC_NOT_FOUND=404
#org.apache.catalina.connector.Response#sendError(int, java.lang.String)
public void sendError(int status, String message) throws IOException {
if (isCommitted()) {
throw new IllegalStateException
(sm.getString("coyoteResponse.sendError.ise"));
}
// Ignore any call from an included servlet
if (included) {
return;
}
setError();
#设置响应状态为404
getCoyoteResponse().setStatus(status);
getCoyoteResponse().setMessage(message);
// Clear any data content that has been buffered
resetBuffer();
// Cause the response to be finished (from the application perspective)
setSuspended(true);
}
结果Tomcat的层层处理,进入到 org.apache.catalina.core.StandardHostValve
可以看到location=/error,进行forward到该路径,这个就是白页上出现/error的原因。
@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, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
#返回给error的view
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
进入org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration.StaticView#render的方法中
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
throws Exception {
if (response.isCommitted()) {
String message = getMessage(model);
logger.error(message);
return;
}
response.setContentType(TEXT_HTML_UTF8.toString());
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("Whitelabel Error Page
").append(
"This application has no explicit mapping for /error, so you are seeing this as a fallback.
")
.append("").append(timestamp).append("")
.append("There was an unexpected error (type=").append(htmlEscape(model.get("error")))
.append(", status=").append(htmlEscape(model.get("status"))).append(").");
if (message != null) {
builder.append("").append(htmlEscape(message)).append("");
}
if (trace != null) {
builder.append("").append(htmlEscape(trace)).append("");
}
builder.append("");
response.getWriter().append(builder.toString());
}
总结:出现白页的主要流程是在DispatcherServlet中的doDispatch方法中,根据得到的handler没有在ResourceHttpRequestHandler的方法中找到resource,然后将response的状态设置为404。然后forward到/error路径,重新进入doDispatch方法寻找可以匹配的方法,和spring boot自动装配的用于错误处理的BasicErrorController的方法,然后找到viewName为error的staticView,在view中有关于所有的错误信息的定义。然后返回给首页。
#以下为debug到doDispatch时的调用栈信息
getHandler:1233, DispatcherServlet (org.springframework.web.servlet)
doDispatch:1016, DispatcherServlet (org.springframework.web.servlet)
doService:943, DispatcherServlet (org.springframework.web.servlet)
processRequest:1006, FrameworkServlet (org.springframework.web.servlet)
doGet:898, FrameworkServlet (org.springframework.web.servlet)
service:634, HttpServlet (javax.servlet.http)
service:883, FrameworkServlet (org.springframework.web.servlet)
service:741, HttpServlet (javax.servlet.http)
internalDoFilter:231, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:100, RequestContextFilter (org.springframework.web.filter)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilter:103, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilter:103, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
invoke:712, ApplicationDispatcher (org.apache.catalina.core)
processRequest:461, ApplicationDispatcher (org.apache.catalina.core)
doForward:384, ApplicationDispatcher (org.apache.catalina.core)
forward:312, ApplicationDispatcher (org.apache.catalina.core)
custom:394, StandardHostValve (org.apache.catalina.core)
status:253, StandardHostValve (org.apache.catalina.core)
invoke:175, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:74, StandardEngineValve (org.apache.catalina.core)
service:343, CoyoteAdapter (org.apache.catalina.connector)
service:373, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:868, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1590, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)
那该如和解决那?
#定义拦截器
public class CustomHandlerInterceptor implements HandlerInterceptor {
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
if (modelAndView != null) {
modelAndView.setViewName("err");
// modelAndView.setViewName("custiomError");
}
}
}
#注册拦截器
@Bean
WebMvcConfigurerAdapter webMvcConfigurerAdapter() {
return new WebMvcConfigurerAdapter() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CustomHandlerInterceptor()).addPathPatterns("/**");
super.addInterceptors(registry);
}
};
}
#yaml中的配置
spring:
mvc:
view:
prefix: G:\code\visitstatistic\src\main\resources\templates\
suffix: .html
#配置的bean如下
@Bean
WebServerFactoryCustomizer<ConfigurableWebServerFactory> embeddedServletContainerCustomizer() {
return container->{
ErrorPage errorPage = new ErrorPage(HttpStatus.NOT_FOUND, "/404");
container.addErrorPages(errorPage);
};
}
#配置的getMapping为
@GetMapping("404")
public String query() {
return "com.demo.visitstatistic.NotController.query 404 页面";
}
返回的页面如下:
结果:返回给路径为404的方法的返回值。
流程为:count/reuse/2——》/404(ErrorPage中的配置的路径)
#配置的方法为
@GetMapping("error")
@ResponseBody
public String errorPath() {
return "com.demo.visitstatistic.CustomErrorController.errorPath ";
}
#配置SimpleUrlHandlerMapping
@Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
return new SimpleUrlHandlerMapping();
}
@RestController
public class NotController {
@Autowired
private SimpleUrlHandlerMapping simpleUrlHandlerMapping;
@PostConstruct
public void init() {
Map<String, Object> map = new HashMap<>();
map.put(ERROR_PATH, this);
simpleUrlHandlerMapping.setUrlMap(map);
simpleUrlHandlerMapping.initApplicationContext();
}
@GetMapping("error")
public String error() {
return "com.demo.visitstatistic.NotController.error";
}
}
为啥没有生效?SimpleUrlHandlerMapping如何使用?原理是啥?它的同类有哪些?
修补方法二:仿照默认的controller,继承BasicErrorController
#自定的controller,继承自ErrorController
@Controller
public class CustomErrorController implements ErrorController {
@Override
public String getErrorPath() {
return ERROR_PATH;
}
@GetMapping("error")
@ResponseBody
public String errorPath() {
return "com.demo.visitstatistic.CustomErrorController.errorPath ";
}
private static final String ERROR_PATH = "/error";
}
参考的博客