How to map servlet to /*, it fails with infinite l

2019-01-28 22:48发布

问题:

I would like to map my servlet to /*, but it failed with an infinite loop.

<servlet>
    <servlet-name>helloServlet</servlet-name>
    <servlet-class>my.HelloServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>helloServlet</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

The java code is:

public class HelloServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response){
        request.getRequestDispatcher("/WEB-INF/jsps/hello.jsp").forward(request, response);
    }

}

If I map to /hello, everything works fine.

As the HelloServlet is mapped to /*, it will also be invoked on RequestDispatcher#forward() and cause the infinite loop.

How is this caused and how can I solve it?

回答1:

This is not possible. The JSP should actually invoke container's builtin JspServlet. But the /* mapping definied by the webapp has a higher precedence.

You need to map the servlet on a more specific URL pattern like /pages/* and create a servlet filter which forwards non-static requests to that servlet. Yes, non-static requests (image/CSS/JS files) are also covered by /*, but they should not be processed by the servlet at all.

Assuming that you've all static resources in /resources folder, the following should do:

<filter>
    <filter-name>filter</filter-name>
    <filter-class>com.example.Filter</filter-class>
</filter>
<filter-mapping>
    <filter-name>filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<servlet>
    <servlet-name>controller</servlet-name>
    <servlet-class>com.example.Controller</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>controller</servlet-name>
    <url-pattern>/pages/*</url-pattern>
</servlet-mapping>

With the following in filter's doFilter():

HttpServletRequest req = (HttpServletRequest) request;
String path = req.getRequestURI().substring(req.getContextPath().length());

if (path.startsWith("/resources")) {
    chain.doFilter(request, response); // Goes to container's own default servlet.
} else {
    request.getRequestDispatcher("/pages" + uri).forward(request, response); // Goes to controller servlet.
}

This takes place fully transparently without any change to /pages in the URL. The forward to the JSP will not trigger the filter or the servlet. Filters do by default not kick in on forwards and the JSP forward path does not match the controller servlet's URL pattern any more.

Alternatively, if you have your own default servlet implementation, then you could map the servlet to / and let it delegate to the default servlet if the request isn't applicable as a front controller request. This is what Spring MVC is doing under the covers. Creating a default servlet is however not a trivial task as it should be capable of responding to conditional requests, caching requests, streaming requests, resume requests, directory listing requests, etecetera.

See also:

  • Difference between / and /* in servlet mapping url pattern
  • How to access static resources when mapping a global front controller servlet on /*
  • Design Patterns web based applications


回答2:

This is possible duplicate to Servlet Filter going in infinite loop when FORWARD used in mapping in JSF

Otherwise should check what your JSP contains, it could be that it makes request for css or image files that can result in this behaviour. Also would recommend to try without a wildcard.