Diferença entre / e / * no padrão de URL de mapeamento de servlet

175

O 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>

Meu entendimento é que /*mapeia para http://host:port/context/*.

Que tal /? Com certeza não é http://host:port/contextmapeado apenas para a raiz. De fato, ele aceitará http://host:port/context/hello, mas rejeitará http://host:port/context/hello.jsp.

Alguém pode explicar como é http://host:port/context/hellomapeado?

Candy Chiu
fonte

Respostas:

268

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

O /*em um servlet substitui todos os outros servlets, incluindo todos os servlets fornecidos pelo servletcontainer, como o servlet padrão e o servlet JSP. Qualquer que seja o pedido que você acionar, ele terminará nesse servlet. Portanto, esse é um padrão de URL incorreto para servlets. Normalmente, você gostaria de usar apenas /*um Filter. Ele pode permitir que a solicitação continue para qualquer um dos servlets que estão ouvindo um padrão de URL mais específico, chamando FilterChain#doFilter().

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

O /não substitui nenhum outro servlet. Ele substitui apenas o servlet padrão interno do contêiner de servlet para todas as solicitações que não correspondem a nenhum outro servlet registrado. Normalmente, isso é chamado apenas em recursos estáticos (CSS / JS / image / etc) e em listas de diretórios. O servlet padrão interno do contêiner de servlet também é capaz de lidar com solicitações de cache HTTP, streaming de mídia (áudio / vídeo) e o download de arquivos. Normalmente, você não deseja substituir o servlet padrão, pois, caso contrário, teria que cuidar de todas as suas tarefas, o que não é exatamente trivial (a biblioteca de utilitários JSF OmniFaces tem um exemplo de código aberto ) Portanto, esse também é um padrão de URL incorreto para servlets. Quanto ao motivo pelo qual as páginas JSP não atingem esse servlet, é porque o servlet JSP interno do contêiner será chamado, o que já é mapeado por padrão no padrão de URL mais específico *.jsp.

<url-pattern></url-pattern>

Depois, há também o padrão de URL de string vazio . Isso será chamado quando a raiz de contexto for solicitada. Isso é diferente da <welcome-file>abordagem que não é chamada quando qualquer subpasta é solicitada. Esse provavelmente é o padrão de URL que você está procurando, caso deseje um " servlet da página inicial ". Eu só tenho que admitir que esperaria intuitivamente que o padrão de URL de string vazio e o padrão de URL de barra /fossem definidos exatamente ao contrário, para que eu entenda que muitos iniciantes ficaram confusos com isso. Mas é o que é.

Controlador Frontal

No caso de você realmente pretende ter um servlet controlador de frente, então você tinha melhor mapeá-lo em um padrão de URL mais específico, como *.html, *.do, /pages/*, /app/*, etc. Você pode esconder os recursos estáticos padrão de URL controlador frontal e tampa sobre um padrão de URL comum como /resources/*, /static/*etc, com a ajuda de um filtro de servlet. Consulte também Como impedir que recursos estáticos sejam manipulados pelo servlet do controlador frontal que é mapeado em / * . Deve-se notar que o Spring MVC possui um servlet de recursos estáticos embutido; é por isso que você pode mapear seu controlador frontal /se configurar um padrão de URL comum para recursos estáticos no Spring. Consulte também Como lidar com conteúdo estático no Spring MVC?

BalusC
fonte
9
Obrigado. Após algumas pesquisas, gostaria de esclarecer um ponto sutil. / sobrescreve o servlet padrão que o servidor da web instala. Por exemplo, o Tomcat instala um DefaultServlet que serve recursos estáticos. O uso / se livra do servlet padrão como um efeito colateral (provavelmente indesejável).
Candy Chiu
Bem, eu não chamaria isso de "substituição", mas "substituição". Pode ser útil substituir o servlet padrão assim.
BalusC
1
<url-pattern> </url-pattern> gera um erro: <url-pattern> inválido no mapeamento de servlet
slim
A mensagem de erro era do tomcat, não do meu IDE; no entanto, estou usando Tomcat 6, de modo que é provavelmente a questão;)
magro
2
@BalusC, você pode me dizer qual /**padrão indica?
Sajib Acharya
45

Eu gostaria de complementar a resposta do BalusC com as regras de mapeamento e um exemplo.

Regras de mapeamento da especificação Servlet 2.5:

  1. URL exato do mapa
  2. Mapear caminhos curinga
  3. Extensões de mapa
  4. Mapear para o servlet padrão

No nosso exemplo, existem três servlets. / é o servlet padrão instalado por nós. O Tomcat instala dois servlets para servir jsp e jspx. Então, para mapearhttp://host:port/context/hello

  1. Nenhum servlet de URL exato instalado, a seguir.
  2. Nenhum servlet de caminhos curinga instalado, a seguir.
  3. Não corresponde a nenhuma extensão, a seguir.
  4. Mapeie para o servlet padrão, retorne.

Mapear http://host:port/context/hello.jsp

  1. Nenhum servlet de URL exato instalado, a seguir.
  2. Nenhum servlet de caminhos curinga instalado, a seguir.
  3. Servlet de extensão encontrado, retorne.
Candy Chiu
fonte
25

Talvez você precise saber como os URLs também são mapeados, pois sofri 404por horas. Existem dois tipos de manipuladores que manipulam solicitações. BeanNameUrlHandlerMappinge SimpleUrlHandlerMapping. Quando definimos a servlet-mapping, estamos usando SimpleUrlHandlerMapping. Uma coisa que precisamos saber é que esses dois manipuladores compartilham uma propriedade comum denominada alwaysUseFullPathpadrão false.

falseaqui significa que o Spring não usará o caminho completo para mapear um URL para um controlador. O que isso significa? Significa quando você define um servlet-mapping:

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

o manipulador realmente usará a *peça para encontrar o controlador. Por exemplo, o seguinte controlador enfrentará um 404erro quando você o solicitar 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) {
        ....
    }
}

É uma combinação perfeita, certo? Mas por que 404. Como mencionado anteriormente, o valor padrão de alwaysUseFullPathé false, o que significa que, em sua solicitação, /api/feature/doSomethingé usado apenas para encontrar um Controlador correspondente, mas não há Controlador que se importe com esse caminho. Você precisa alterar seu URL /perfix/perfix/api/feature/doSomethingou remover perfixda base do MyController @RequestingMapping.

hakunami
fonte
8

Acho que a resposta de Candy está correta. Há uma pequena parte que penso de outra forma.

Para mapear o host: port / context / hello.jsp

  1. Nenhum servlet de URL exato instalado, a seguir.
  2. Servlets de caminhos curinga encontrados , retorno.

Eu acredito que por que "/ *" não corresponde a host: port / context / hello porque trata "/ hello" como um caminho em vez de um arquivo (já que não possui uma extensão).

ele Ele
fonte
2

A diferença essencial entre /*e /é que um servlet com mapeamento /*será selecionado antes de qualquer servlet com um mapeamento de extensão (como *.html), enquanto um servlet com mapeamento /será selecionado somente depois que os mapeamentos de extensão forem considerados (e será usado para qualquer solicitação que não seja ' não corresponde a mais nada - é o "servlet padrão").

Em particular, um /*mapeamento sempre será selecionado antes de um /mapeamento. Isso impede que qualquer solicitação alcance o servlet padrão do contêiner.

Qualquer um deles será selecionado somente após os mapeamentos de servlet que são correspondências exatas (como /foo/bar) e aqueles que são mapeamentos de caminho mais longos que /*(como /foo/*). Observe que o mapeamento de string vazio é uma correspondência exata para a raiz de contexto ( http://host:port/context/).

Consulte o Capítulo 12 da Java Servlet Specification, disponível na versão 3.1 em http://download.oracle.com/otndocs/jcp/servlet-3_1-fr-eval-spec/index.html .

Robert Tupelo-Schneck
fonte