如何将根路径下的404 URL映射到指定Servlet(Tomcat 8.5)
Hey there! Let's break down how to solve this Tomcat 8.5 mapping problem exactly as you need it. We'll start with the ideal solution that targets only root-level unknown URLs, then cover the fallback approach if that doesn't fit your setup.
Solution 1: Target Root-Level Unknown URLs (Recommended)
This uses Tomcat's error page mechanism to intercept 404s only for root-level paths and route them to your UnhandledUrlsServlet, while leaving deeper paths to return the default 404.
Step 1: Configure Error Page Forwarding
Add this to your web.xml to send 404 errors to your servlet:
<error-page> <error-code>404</error-code> <location>/UnhandledUrlsServlet</location> </error-page>
Step 2: Map Your Servlet
Set up your servlet with an exact mapping so it only responds to the error forward (not direct requests unless you want it to):
<servlet> <servlet-name>UnhandledUrlsServlet</servlet-name> <servlet-class>com.yourpackage.UnhandledUrlsServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>UnhandledUrlsServlet</servlet-name> <url-pattern>/UnhandledUrlsServlet</url-pattern> </servlet-mapping>
Step 3: Add Path Logic to the Servlet
In your servlet's doGet/doPost methods, grab the original request URI (since the error forward changes the URI) and check if it's a root-level path:
// Get the original URL that triggered the 404 String originalUri = (String) request.getAttribute("javax.servlet.error.request_uri"); // Strip off the context path (skip this if your app is deployed at root) String path = originalUri.substring(request.getContextPath().length()); // Split the path to check its depth String[] pathSegments = path.split("/"); // Root-level paths like "/unknownUrl1" split into ["", "unknownUrl1"] (length 2) if (pathSegments.length == 2 && !path.equals("/")) { // Your logic to handle the unhandled root-level URL response.getWriter().write("Handling unhandled root URL: " + path); } else { // Deeper path? Send back the default 404 response.sendError(HttpServletResponse.SC_NOT_FOUND); }
Now when someone hits /unknownUrl1 (no existing servlet/resource matches, so 404 is triggered), your servlet takes over. But /unknown/somethingElse will still return Tomcat's default 404 page.
Solution 2: Fallback - Catch All Requests and Filter Manually
If the error page approach doesn't work for your setup, you can map your servlet to catch all requests, then filter out which ones to handle.
Step 1: Map Servlet to All Requests
Add this to web.xml—note this will override all other servlet mappings, so you'll need to exclude known paths manually:
<servlet> <servlet-name>UnhandledUrlsServlet</servlet-name> <servlet-class>com.yourpackage.UnhandledUrlsServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>UnhandledUrlsServlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
Step 2: Filter Paths in the Servlet
In your servlet, first exclude any paths that already have handlers (like APIs, static resources, or other servlets), then check for root-level unknown paths:
String path = request.getRequestURI().substring(request.getContextPath().length()); // List of paths that already have existing handlers List<String> handledPaths = Arrays.asList("/api", "/static", "/existing-servlet"); boolean isKnownPath = handledPaths.stream().anyMatch(path::startsWith); // Forward known paths to their normal handlers if (isKnownPath) { request.getRequestDispatcher(path).forward(request, response); return; } // Check if it's a root-level unknown path String[] pathSegments = path.split("/"); if (pathSegments.length == 2 && !path.equals("/")) { // Handle the root-level unhandled URL response.getWriter().write("Handling unhandled root URL: " + path); } else { // Return default 404 for deeper paths response.sendError(HttpServletResponse.SC_NOT_FOUND); }
The downside here is you'll need to keep the handledPaths list updated as you add new servlets or resources, but it's a solid fallback if you need more direct control.
Quick Notes
- If your app is deployed at a non-root context (like
/myapp), make sure you always strip the context path when checking paths—otherwise your logic will break. - Tomcat 8.5 prioritizes exact servlet mappings over wildcard mappings, so any exact paths you have set up will still work even with the
/*mapping in Solution 2. - Test thoroughly to make sure existing resources/servlets aren't being accidentally intercepted!
内容的提问来源于stack exchange,提问作者MTilsted

