Search code examples
javatomcatservletsundertow

About Servlet binding patterns and default servlet


I have a Servlet I want to bind to "/" pattern. After that, all works fine for all URLs including application root but I also need the default servlet for static files support.

After I add it into web.xml this way I got 404 error at the application root URL, but all other URLs are still successfully handled by the Servlet assigned to "/".

<servlet-mapping>
  <servlet-name>default</servlet-name>
  <url-pattern>*.html</url-pattern>
  <url-pattern>*.css</url-pattern>
  <url-pattern>*.js</url-pattern>
<servlet-mapping>

After I bind the Servlet also to "" pattern it works fine. How I understand this pattern is related to the root URL of the application.

Could you help me to find out the reasons for such behavior?

P.S. I check the behavior with TomCat and Undertow.


Solution

  • I reproduced your issue using Apache Tomcat 9.0.21 and following servlet:

    package my.sample;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class Servlet1 extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.getWriter().write("servlet1: " + req.getServletPath());
        }
    }
    

    And web.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
        id="myApp" version="4.0">
        <display-name>myWebApp</display-name>
        <welcome-file-list>
            <welcome-file>index.html</welcome-file>
        </welcome-file-list>
        <servlet>
            <servlet-name>servlet1</servlet-name>
            <servlet-class>my.sample.Servlet1</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>servlet1</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
        <servlet-mapping>
            <servlet-name>default</servlet-name>
            <url-pattern>*.html</url-pattern>
            <url-pattern>*.css</url-pattern>
            <url-pattern>*.js</url-pattern>
        </servlet-mapping>
    </web-app>
    

    Opening the URL http://localhost:8080/myWebApp/ results in error 404 as described by you. Other URLs not matching any default servlet url-pattern are handled by my servlet1 and produce output like servlet1: /hello for example when opening http://localhost:8080/myWebApp/hello

    When removing the <url-pattern>*.html</url-pattern> from default servlet mapping, your issue is gone and I get servlet1: / as response when opening http://localhost:8080/myWebApp/.

    When adding the <url-pattern>*.html</url-pattern> to servlet1 servlet mapping I get servlet1: /index.html as response when opening http://localhost:8080/myWebApp/.

    This suggests the reason for your issue is that path / is internally forwarded to the welcome-file index.html which perfectly matches the *.html url-pattern configured for the default servlet in your case - and thus the default servlet is chosen to handle the / request path. If an index.html is abscent, the default servlet sends the 404 error observed by you.