Spring Boot (스프링 부트) - undertow의 에러페이지 처리 문제
<< 환경 >>
Java Open JDK 1.8
Spring Boot 1.5.2.RELEASE
NestedServletException이 Undertow의 에러처리를 요구하여 WAS container에 도달하기 전에 Spring에서 먼저 처리하는 것으로 보이는 현상 - 404에러 발생시 관련 로그없이 바로 에러페이지를 호출
관련 로그를 확인하기 위하여 에러처리 템플릿(error template) 404.html, 500.html 등을 모두 제거 후 테스트 진행
2020-12-19 03:19:04.721 ERROR 11852 --- [ XNIO-2 task-6] org.thymeleaf.TemplateEngine : [THYMELEAF][XNIO-2 task-6] Exception processing template "error": Error resolving template "error", template might not exist or might not be accessible by any of the configured Template Resolvers
2020-12-19 03:19:04.722 ERROR 11852 --- [ XNIO-2 task-6] io.undertow.request : UT005023: Exception handling request to /error
java.lang.RuntimeException: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template "error", template might not exist or might not be accessible by any of the configured Template Resolvers
at io.undertow.servlet.spec.HttpServletResponseImpl.doErrorDispatch(HttpServletResponseImpl.java:159)
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:295)
at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:211)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:809)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template "error", template might not exist or might not be accessible by any of the configured Template Resolvers
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)
at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:81)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.SessionRestoringHandler.handleRequest(SessionRestoringHandler.java:119)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:274)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchToPath(ServletInitialHandler.java:209)
at io.undertow.servlet.spec.RequestDispatcherImpl.error(RequestDispatcherImpl.java:480)
at io.undertow.servlet.spec.RequestDispatcherImpl.error(RequestDispatcherImpl.java:405)
at io.undertow.servlet.spec.HttpServletResponseImpl.doErrorDispatch(HttpServletResponseImpl.java:157)
... 14 common frames omitted
Caused by: org.thymeleaf.exceptions.TemplateInputException: Error resolving template "error", template might not exist or might not be accessible by any of the configured Template Resolvers
at org.thymeleaf.TemplateRepository.getTemplate(TemplateRepository.java:246)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1104)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1060)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1011)
at org.thymeleaf.spring4.view.ThymeleafView.renderFragment(ThymeleafView.java:335)
at org.thymeleaf.spring4.view.ThymeleafView.render(ThymeleafView.java:190)
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1282)
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
... 30 common frames omitted
2020-12-19 03:19:04.723 ERROR 11852 --- [ XNIO-2 task-6] io.undertow.servlet.request : UT015012: Failed to generate error page /error for original exception: null. Generating error page resulted in a 500.
java.lang.RuntimeException: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template "error", template might not exist or might not be accessible by any of the configured Template Resolvers
at io.undertow.servlet.spec.HttpServletResponseImpl.doErrorDispatch(HttpServletResponseImpl.java:159)
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:295)
at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:211)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:809)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template "error", template might not exist or might not be accessible by any of the configured Template Resolvers
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)
at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:81)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.SessionRestoringHandler.handleRequest(SessionRestoringHandler.java:119)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:274)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchToPath(ServletInitialHandler.java:209)
at io.undertow.servlet.spec.RequestDispatcherImpl.error(RequestDispatcherImpl.java:480)
at io.undertow.servlet.spec.RequestDispatcherImpl.error(RequestDispatcherImpl.java:405)
at io.undertow.servlet.spec.HttpServletResponseImpl.doErrorDispatch(HttpServletResponseImpl.java:157)
... 14 common frames omitted
Caused by: org.thymeleaf.exceptions.TemplateInputException: Error resolving template "error", template might not exist or might not be accessible by any of the configured Template Resolvers
at org.thymeleaf.TemplateRepository.getTemplate(TemplateRepository.java:246)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1104)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1060)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1011)
at org.thymeleaf.spring4.view.ThymeleafView.renderFragment(ThymeleafView.java:335)
at org.thymeleaf.spring4.view.ThymeleafView.render(ThymeleafView.java:190)
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1282)
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
... 30 common frames omitted
|
RuntimeException, NotFoundException, Internal Server Error 발생시 Undertow가 thymeleaf template을 찾는 것을 확인
에러 발생시 Undertow는 ServletInitialHandler에서
1. 맵핑된 에러 탐색
2. 기본 에러페이지 호출
3. 지정된 에러페이지 호출
를 하고 있는 것으로 추정
이를 우회하기 위한 방법으로
1. application 실행 파일에 Bean객체 포함
1
2
3
4
5
6
7
8
9
10
|
@Bean
EmbeddedServletContainerCustomizer errorPageCustomizer() {
return (container -> {
container.addErrorPages(
new ErrorPage(RuntimeException.class, "/error"),
new ErrorPage(HttpStatus.NOT_FOUND, "/error"),
new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error")
);
});
}
|
cs |
또는
1. CustomServerProperties extends ServerProperties 파일 생성 후 ServletContextInitializer, EmbeddedServletContainerCustomizer를 구현하는 설정 파일에 @Bean객체로 추가
[ CustomTestServerProperties.java ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
import java.util.HashSet;
import java.util.Set;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.web.servlet.ErrorPage;
import org.springframework.http.HttpStatus;
/**
* Created on 2020-12-18.
*/
public class CustomTestServerProperties extends ServerProperties {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
super.customize(container);
Set<ErrorPage> errorPageSet = new HashSet<>();
errorPageSet.add(new ErrorPage(RuntimeException.class, "/error"));
errorPageSet.add(new ErrorPage(HttpStatus.NOT_FOUND, "/error"));
// errorPageSet.add(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error"));
container.setErrorPages(errorPageSet);
}
}
|
cs |
[ WebConfigurer.java ] - ServletContextInitializer, EmbeddedServletContainerCustomizer를 구현
1
2
3
4
|
@Bean
public CustomTestServerProperties customTestServerProperties() {
return new CustomTestServerProperties();
}
|
cs |
2. CustomErrorController 생성 후 위에서 mapping된 ErrorController를 찾을 수 있도록 유도
[ CustomErrorController.java ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* Created on 2020-12-18.
*
* @return error template
*/
@Controller
public class CustomErrorController implements ErrorController {
private final Logger log = LoggerFactory.getLogger(CustomErrorController.class);
private static final String ERROR_PATH = "/error";
@Override
public String getErrorPath() {
return ERROR_PATH;
}
@ResponseStatus(HttpStatus.FOUND) // status code : 302
@RequestMapping(value = "/error", method = RequestMethod.GET, produces = "text/html")
public String handleErrorPage(HttpServletRequest request, Model model) {
log.info("---------------------------------------------------");
log.info("Controller: " + this.getClass().getName().toString());
log.info("---------------------------------------------------");
return "error/error";
}
}
|
cs |
3. CustomErrorController에서 지정한 error page를 찾을 수 있도록 thymeleaf 설정 추가
[ application.yml ]
1
2
3
4
5
|
spring:
thymeleaf:
cache: false
prefix: /
suffix: .html
|
cs |
참고
https://github.com/spring-projects/spring-boot/issues/2692
'스프링 (Spring) > Spring Boot' 카테고리의 다른 글
[Spring Boot 실습 #4] 초기 데이터 생성 - CommandLineRunner 활용 (1) (0) | 2021.05.02 |
---|---|
[Spring Boot 실습 #3] Runtime에 WAR파일 내부 json 파일 읽기 (3) - 테스트 (0) | 2021.02.01 |
[Spring Boot 실습 #3] Runtime에 WAR파일 내부 json 파일 읽기 (2) - 테스트 (0) | 2021.01.20 |
[Spring Boot 실습 #3] Runtime에 WAR파일 내부 json 파일 읽기 (1) (0) | 2021.01.20 |
[Spring Boot 실습 #2] undertow - HTTP method 제한 (0) | 2020.12.24 |