Diferencia entre / y / * en el patrón de URL de mapeo de servlet

175

El código familiar:

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

<servlet-mapping>
    <servlet-name>main</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

Tengo entendido que los /*mapas a http://host:port/context/*.

¿Qué tal /? Seguro que no se http://host:port/contextasigna solo a la raíz. De hecho, aceptará http://host:port/context/hello, pero rechazará http://host:port/context/hello.jsp.

¿Alguien puede explicar cómo se http://host:port/context/hellomapea?

Candy Chiu
fuente

Respuestas:

268

<url-pattern>/*</url-pattern>

El /*en un servlet anula todos los demás servlets, incluidos todos los servlets proporcionados por el contenedor de servlet, como el servlet predeterminado y el servlet JSP. Cualquier solicitud que dispare, terminará en ese servlet. Por lo tanto, este es un mal patrón de URL para servlets. Por lo general, desea utilizar /*en una Filterúnica. Puede permitir que la solicitud continúe a cualquiera de los servlets que escuchan en un patrón de URL más específico llamando FilterChain#doFilter().

<url-pattern>/</url-pattern>

El /no anula ningún otro servlet. Solo reemplaza el servlet predeterminado incorporado del contenedor de servlet para todas las solicitudes que no coinciden con ningún otro servlet registrado. Esto normalmente solo se invoca en recursos estáticos (CSS / JS / image / etc) y listados de directorios. El servlet por defecto incorporado del servletcontainer también es capaz de manejar solicitudes de caché HTTP, transmisión de medios (audio / video) y reanudación de descarga de archivos. Por lo general, no desea anular el servlet predeterminado ya que de lo contrario tendría que ocuparse de todas sus tareas, lo que no es exactamente trivial (la biblioteca de utilidades JSF OmniFaces tiene un ejemplo de código abierto ) Por lo tanto, este también es un mal patrón de URL para servlets. En cuanto a por qué las páginas JSP no llegan a este servlet, se debe a que se invocará el servlet JSP incorporado del contenedor de servlet, que ya está asignado de forma predeterminada en el patrón de URL más específico *.jsp.

<url-pattern></url-pattern>

Luego también está el patrón de URL de cadena vacía . Esto se invocará cuando se solicite la raíz de contexto. Esto es diferente del <welcome-file>enfoque de que no se invoca cuando se solicita una subcarpeta. Este es probablemente el patrón de URL que está buscando en caso de que desee un " servlet de página de inicio ". Solo tengo que admitir que intuitivamente espero que el patrón de URL de cadena vacía y el patrón de barra diagonal /se definan exactamente al revés, por lo que puedo entender que muchos entrantes se confundieron con esto. Pero es lo que es.

Controlador frontal

En caso de que en realidad la intención de tener un servlet controlador frontal, entonces sería mejor mapa en un patrón de URL más específica como *.html, *.do, /pages/*, /app/*, etc Usted puede ocultar los recursos estáticos patrón URL controlador frontal y la cubierta en un patrón de URL común como /resources/*, /static/*etc. con la ayuda de un filtro de servlet. Consulte también Cómo evitar que los recursos estáticos sean manejados por el servlet del controlador frontal que se asigna en / * . Se debe tener en cuenta que Spring MVC tiene un servlet de recursos estáticos incorporado, por lo que podría asignar su controlador frontal /si configura un patrón de URL común para recursos estáticos en Spring. Consulte también ¿Cómo manejar el contenido estático en Spring MVC?

BalusC
fuente
9
Gracias. Después de un poco de investigación, me gustaría aclarar un punto sutil. / sobrescribe el servlet predeterminado que instala el servidor web. Por ejemplo, Tomcat instala un DefaultServlet que sirve recursos estáticos. El uso / elimina el servlet predeterminado como un efecto secundario (probablemente no deseado).
Candy Chiu
Bueno, no lo llamaría "sobrescribir", sino "reemplazar". Puede ser útil reemplazar el servlet predeterminado de esa manera.
BalusC
1
<url-pattern> </url-pattern> arroja un error: <url-pattern> no válido en la asignación de servlet
delgado
El mensaje de error era de tomcat, no de mi IDE; Sin embargo, estoy usando Tomcat 6, por lo que es probablemente el tema;)
delgada
2
@BalusC, ¿puede decirme qué /**patrón indica?
Sajib Acharya
45

Me gustaría complementar la respuesta de BalusC con las reglas de mapeo y un ejemplo.

Reglas de mapeo de la especificación Servlet 2.5:

  1. URL exacta del mapa
  2. Mapa de caminos comodín
  3. Extensiones de mapa
  4. Asignar al servlet predeterminado

En nuestro ejemplo, hay tres servlets. / es el servlet predeterminado instalado por nosotros. Tomcat instala dos servlets para servir jsp y jspx. Entonces para mapearhttp://host:port/context/hello

  1. No hay servlets URL exactos instalados, a continuación.
  2. No hay servlets de comodines instalados, a continuación.
  3. No coincide con ninguna extensión, a continuación.
  4. Mapa al servlet predeterminado, volver.

Para asignar http://host:port/context/hello.jsp

  1. No hay servlets URL exactos instalados, a continuación.
  2. No hay servlets de comodines instalados, a continuación.
  3. Servlet de extensión encontrado, devolución.
Candy Chiu
fuente
25

Quizás también necesites saber cómo se mapean las URL, ya que sufrí 404durante horas. Hay dos tipos de manejadores que manejan las solicitudes. BeanNameUrlHandlerMappingy SimpleUrlHandlerMapping. Cuando definimos a servlet-mapping, estamos usando SimpleUrlHandlerMapping. Una cosa que necesitamos saber es que estos dos manejadores comparten una propiedad común llamada alwaysUseFullPathque por defecto es false.

falseaquí significa que Spring no usará la ruta completa para asignar una url a un controlador. Qué significa eso? Significa cuando define un servlet-mapping:

<servlet-mapping>
    <servlet-name>viewServlet</servlet-name>
    <url-pattern>/perfix/*</url-pattern>
</servlet-mapping>

el controlador realmente usará la *parte para encontrar el controlador. Por ejemplo, el siguiente controlador enfrentará un 404error cuando lo solicite usando/perfix/api/feature/doSomething

@Controller()
@RequestMapping("/perfix/api/feature")
public class MyController {
    @RequestMapping(value = "/doSomething", method = RequestMethod.GET) 
    @ResponseBody
    public String doSomething(HttpServletRequest request) {
        ....
    }
}

Es una combinación perfecta, ¿verdad? Pero por qué 404. Como se mencionó anteriormente, el valor predeterminado de alwaysUseFullPathes falso, lo que significa que en su solicitud, solo /api/feature/doSomethingse usa para encontrar un controlador correspondiente, pero no hay ningún controlador que se preocupe por esa ruta. Debe cambiar su URL /perfix/perfix/api/feature/doSomethingo eliminarla perfixde la base MyController @RequestingMapping.

hakunami
fuente
8

Creo que la respuesta de Candy es mayormente correcta. Hay una pequeña parte, creo que de lo contrario.

Para asignar host: puerto / contexto / hello.jsp

  1. No hay servlets URL exactos instalados, a continuación.
  2. Encontrado caminos comodines servlets , volver.

Creo que por eso "/ *" no coincide con host: port / context / hello porque trata "/ hello" como una ruta en lugar de un archivo (ya que no tiene una extensión).

jeje
fuente
2

La diferencia esencial entre /*y /es que un servlet con asignación /*se seleccionará antes que cualquier servlet con una asignación de extensión (como *.html), mientras que un servlet con asignación /se seleccionará solo después de considerar las asignaciones de extensión (y se utilizará para cualquier solicitud que no t coincide con cualquier otra cosa --- es el "servlet predeterminado").

En particular, una /*asignación siempre se seleccionará antes de una /asignación. Tener cualquiera evita que las solicitudes lleguen al servlet predeterminado del contenedor.

Cualquiera de los dos se seleccionará solo después de las asignaciones de servlet que son coincidencias exactas (me gusta /foo/bar) y aquellas que son asignaciones de ruta más largas que /*(me gusta /foo/*). Tenga en cuenta que la asignación de cadena vacía es una coincidencia exacta para la raíz de contexto ( http://host:port/context/).

Consulte el Capítulo 12 de la Especificación de Servlet Java, disponible en la versión 3.1 en http://download.oracle.com/otndocs/jcp/servlet-3_1-fr-eval-spec/index.html .

Robert Tupelo-Schneck
fuente