+1

Spring Boot In Action: 404 not found.

I. Giới thiệu

Tình huống xảy ra được miêu tả như dưới đây.

Đầu tiên bạn có một Spring Application với cấu trúc thư mục như sau: image.png

Và được run thành công ready trên port 8487: image.png

Tuy nhiên khi bạn thực hiện việc gọi API thì HTTP response status là 404? Ủa 😂😂?

image.png

II. Phân tích vấn đề

Chúng ta muốn biết vì sao xảy ra lỗi 404 thì chúng ta phải hình dung được flow của một HTTP request thực thi trong ứng dụng Spring Boot như thế nào trước.

image.png

Khi Spring Boot application nhận được một HTTP request thì có một thằng gọi là DispatcherServlet sẽ dispatching những request này đến các Controller tương ứng. Sau khi tìm được Controller sẽ phải tìm một handler method có thể handle request đó. Nếu tìm thấy handler method phù hợp thì DispatcherServlet sẽ gọi handler method và trả về response.

Handler method ở đây là method được annotated với @RequestMapping annotation hay @GetMapping annotation với HTTP request method (GET/POST..) và path cụ thể.

#1
@RequestMapping(value = "/404", method = RequestMethod.GET)
public Object demo404NotFound(){
    return "404 not found";
}

#2
@GetMapping("/404")
public Object demo404NotFound(){
    return "404 not found";
}

Tuy nhiên để một request gọi được vào ứng dụng thì Spring Boot cần có một vài bước tiền xử lý trong quá trình startup application như là initHandlerMethods. Phương thức initHandlerMethods chịu trách nhiệm cho việc khởi tạo tất cả mọi handler methods trong Spring Boot.

image.png

image.png

Phương thức này ban đầu sẽ đi tìm những beans trong ApplicationContext annotated với @Controller annotation hay chứa @RequestMapping annotation. Đến đây nếu để ý chúng ta sẽ phát hiện ra nguyên nhân gây nên lỗi 404 kể trên đó là _404Controller bean không được tìm thấy và đây chính là thủ phạm.

image.png

Nhưng chúng ta sẽ không chỉ dừng phân tích lại ở đó, sau khi phương thức initHandlerMethods tìm được bean thõa mãn yêu cầu như @Controller/ @RestController.. thì với mỗi bean tìm được, phương thức đi tìm tiếp những method annotated với @RequestMapping annotation và add vào registry để sử dụng.

private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

image.png

image.png

image.png

image.png

Callstack

register:654, AbstractHandlerMethodMapping$MappingRegistry (org.springframework.web.servlet.handler)
registerHandlerMethod:332, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler)
registerHandlerMethod:420, RequestMappingHandlerMapping (org.springframework.web.servlet.mvc.method.annotation)
registerHandlerMethod:76, RequestMappingHandlerMapping (org.springframework.web.servlet.mvc.method.annotation)
lambda$detectHandlerMethods$2:299, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler)
accept:-1, 10885570 (org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$$Lambda$526)
forEach:684, LinkedHashMap (java.util)
detectHandlerMethods:297, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler)
processCandidateBean:266, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler)
initHandlerMethods:225, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler)
afterPropertiesSet:213, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler)
afterPropertiesSet:205, RequestMappingHandlerMapping (org.springframework.web.servlet.mvc.method.annotation)
invokeInitMethods:1863, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
initializeBean:1800, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:620, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:542, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:335, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 1896232624 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$227)
getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:333, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:208, AbstractBeanFactory (org.springframework.beans.factory.support)
preInstantiateSingletons:955, DefaultListableBeanFactory (org.springframework.beans.factory.support)
finishBeanFactoryInitialization:918, AbstractApplicationContext (org.springframework.context.support)
refresh:583, AbstractApplicationContext (org.springframework.context.support)
refresh:147, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
refresh:734, SpringApplication (org.springframework.boot)
refreshContext:408, SpringApplication (org.springframework.boot)
run:308, SpringApplication (org.springframework.boot)
run:1306, SpringApplication (org.springframework.boot)
run:1295, SpringApplication (org.springframework.boot)
main:14, SpringBean404Application (com.logbasex)

Sau khi xong phần startup application và initHandlerMethods thì chúng ta sẽ tiến hành cắm debugging point ở controller và call API để xem cách Spring Boot nhận và xử lý HTTP request như thế nào.

image.png

image.png

Callstack:

createWithResolvedBean:372, HandlerMethod (org.springframework.web.method)
getHandlerInternal:384, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler)
getHandlerInternal:125, RequestMappingInfoHandlerMapping (org.springframework.web.servlet.mvc.method)
getHandlerInternal:67, RequestMappingInfoHandlerMapping (org.springframework.web.servlet.mvc.method)
getHandler:498, AbstractHandlerMapping (org.springframework.web.servlet.handler)
getHandler:1264, DispatcherServlet (org.springframework.web.servlet)
doDispatch:1046, DispatcherServlet (org.springframework.web.servlet)
doService:963, DispatcherServlet (org.springframework.web.servlet)
processRequest:1006, FrameworkServlet (org.springframework.web.servlet)
doGet:898, FrameworkServlet (org.springframework.web.servlet)
service:655, HttpServlet (javax.servlet.http)
service:883, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:100, RequestContextFilter (org.springframework.web.filter)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:93, FormContentFilter (org.springframework.web.filter)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:201, CharacterEncodingFilter (org.springframework.web.filter)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:541, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:360, CoyoteAdapter (org.apache.catalina.connector)
service:399, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:890, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1789, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:750, Thread (java.lang)

References

===

Thanks for reading.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.