Por que o Spring MVC responde com um erro 404 e relata “Nenhum mapeamento encontrado para solicitação HTTP com URI em DispatcherServlet”?

Estou escrevendo um aplicativo Spring MVC, implantado no Tomcat. Veja o seguinte exemplo mínimo, completo e verificável :

public class Application extends AbstractAnnotationConfigDispatcherServletInitializer { protected Class[] getRootConfigClasses() { return new Class[] { }; } protected Class[] getServletConfigClasses() { return new Class[] { SpringServletConfig.class }; } protected String[] getServletMappings() { return new String[] { "/*" }; } } 

Onde SpringServletConfig é

 @Configuration @ComponentScan("com.example.controllers") @EnableWebMvc public class SpringServletConfig { @Bean public InternalResourceViewResolver resolver() { InternalResourceViewResolver vr = new InternalResourceViewResolver(); vr.setPrefix("/WEB-INF/jsps/"); vr.setSuffix(".jsp"); return vr; } } 

Finalmente, eu tenho um @Controller no pacote com.example.controllers

 @Controller public class ExampleController { @RequestMapping(path = "/home", method = RequestMethod.GET) public String example() { return "index"; } } 

O nome de contexto do meu aplicativo é Example . Quando envio uma solicitação para

 http://localhost:8080/Example/home 

o aplicativo responde com um status HTTP 404 e registra o seguinte

 WARN osweb.servlet.PageNotFound - No mapping found for HTTP request with URI `[/Example/WEB-INF/jsps/index.jsp]` in `DispatcherServlet` with name 'dispatcher' 

Eu tenho um recurso JSP em /WEB-INF/jsps/index.jsp Eu esperava que o Spring MVC usasse meu controlador para manipular o pedido e encaminhar para o JSP, então por que ele está respondendo com um 404?


Isso deve ser uma postagem canônica para perguntas sobre essa mensagem de aviso.

Seu aplicativo Spring MVC padrão atenderá a todas as solicitações por meio de um DispatcherServlet que você registrou com o contêiner Servlet.

O DispatcherServlet examina seu ApplicationContext e, se disponível, o ApplicationContext registrado com um ContextLoaderListener para beans especiais, ele precisa configurar sua lógica de serviço de solicitação. Esses beans são descritos na documentação .

Indiscutivelmente os mais importantes, beans do tipo HandlerMapping map

solicitações de input para manipuladores e uma lista de pré e pós-processadores (interceptadores de manipulador) com base em alguns critérios cujos detalhes variam de acordo com a implementação de HandlerMapping . A implementação mais popular suporta controladores anotados, mas outras implementações também existem.

O javadoc do HandlerMapping descreve ainda como as implementações devem se comportar.

O DispatcherServlet localiza todos os beans desse tipo e os registra em alguma ordem (pode ser personalizado). Enquanto atende a uma solicitação, o DispatcherServlet percorre esses objects HandlerMapping e testa cada um deles com getHandler para encontrar um que possa manipular a solicitação de input, representada como a HttpServletRequest padrão. A partir de 4.3.x, se não encontrar algum , registra o aviso que você vê

Nenhum mapeamento localizado para solicitação HTTP com URI [/some/path] em DispatcherServlet com nome SomeName

e lança uma NoHandlerFoundException ou confirma imediatamente a resposta com um código de status 404 Not Found.

Por que o DispatcherServlet não encontrou um HandlerMapping que pudesse atender à minha solicitação?

A implementação mais comum do HandlerMapping é RequestMappingHandlerMapping , que manipula o registro de beans @Controller como manipuladores (realmente seus methods anotados @RequestMapping ). Você pode declarar você mesmo um bean desse tipo (com @Bean ou ou outro mecanismo) ou pode usar as opções @Bean . Esses são:

  1. Anote sua class @EnableWebMvc com @EnableWebMvc .
  2. Declare um membro em sua configuração XML.

Como o link acima descreve, ambos irão registrar um bean RequestMappingHandlerMapping (e um monte de outras coisas). No entanto, um HandlerMapping não é muito útil sem um manipulador. RequestMappingHandlerMapping espera alguns beans @Controller então você precisa declará-los também, através de methods @Bean em uma configuração Java ou declarações em uma configuração XML ou através da varredura de componentes de classs anotadas @Controller em qualquer uma delas. Certifique-se de que esses beans estejam presentes.

Se você receber a mensagem de aviso e um erro 404 e tiver configurado todas as @RequestMapping acima corretamente, enviará sua solicitação para o URI incorreto , que não é tratado por um método de manipulador anotado @RequestMapping .

A biblioteca spring-webmvc oferece outras implementações HandlerMapping . Por exemplo, mapas BeanNameUrlHandlerMapping

de URLs para beans com nomes que começam com uma barra (“/”)

e você sempre pode escrever o seu próprio. Obviamente, você precisará garantir que a solicitação que está enviando corresponda a pelo menos um dos manipuladores do object HandlerMapping registrado.

Se você não registrar implícita ou explicitamente nenhum HandlerMapping (ou se detectAllHandlerMappings for true ), o DispatcherServlet registrará alguns padrões . Eles são definidos em DispatcherServlet.properties no mesmo pacote da class DispatcherServlet . Eles são BeanNameUrlHandlerMapping e DefaultAnnotationHandlerMapping (que é semelhante a RequestMappingHandlerMapping mas foi preterido).

Depuração

O Spring MVC registrará os manipuladores registrados por meio de RequestMappingHandlerMapping . Por exemplo, um @Controller como

 @Controller public class ExampleController { @RequestMapping(path = "/example", method = RequestMethod.GET, headers = "X-Custom") public String example() { return "example-view-name"; } } 

registrará o seguinte no nível INFO

 Mapped "{[/example],methods=[GET],headers=[X-Custom]}" onto public java.lang.String com.spring.servlet.ExampleController.example() 

Isso descreve o mapeamento registrado. Quando você vir o aviso de que nenhum manipulador foi encontrado, compare o URI na mensagem com o mapeamento listado aqui. Todas as restrições especificadas no @RequestMapping devem corresponder ao Spring MVC para selecionar o manipulador.

Outras implementações do HandlerMapping registram suas próprias instruções que devem sugerir seus mapeamentos e seus manipuladores correspondentes.

Da mesma forma, ative o registro do Spring no nível DEBUG para ver quais beans o Spring registra. Ele deve relatar quais classs anotadas encontra, quais pacotes são verificados e quais beans são inicializados. Se os que você esperava não estiverem presentes, revise a configuração do ApplicationContext .

Outros erros comuns

Um DispatcherServlet é apenas um típico Servlet Java EE. Você o registra com suas declarações típicas e , ou diretamente por meio de ServletContext#addServlet em um WebApplicationInitializer ou com qualquer mecanismo usado pela boot Spring. Como tal, você deve confiar na lógica de mapeamento de URL especificada na especificação Servlet , consulte o Capítulo 12. Consulte também

  • Como são usados ​​os mapeamentos de url Servlet no web.xml?

Com isso em mente, um erro comum é registrar o DispatcherServlet com um mapeamento de URL de /* , retornando um nome de exibição de um método de manipulador @RequestMapping e esperando que um JSP seja renderizado. Por exemplo, considere um método de manipulador como

 @RequestMapping(path = "/example", method = RequestMethod.GET) public String example() { return "example-view-name"; } 

com um InternalResourceViewResolver

 @Bean public InternalResourceViewResolver resolver() { InternalResourceViewResolver vr = new InternalResourceViewResolver(); vr.setPrefix("/WEB-INF/jsps/"); vr.setSuffix(".jsp"); return vr; } 

você pode esperar que o pedido seja encaminhado para um recurso JSP no caminho /WEB-INF/jsps/example-view-name.jsp . Isso não vai acontecer. Em vez disso, assumindo um nome de contexto de Example , o DisaptcherServlet relatará

Nenhum mapeamento localizado para solicitação HTTP com URI [/Example/WEB-INF/jsps/example-view-name.jsp] em DispatcherServlet com o nome ‘dispatcher’

Como o DispatcherServlet é mapeado para /* e /* corresponde a tudo (exceto correspondências exatas, que têm prioridade mais alta), o DispatcherServlet seria escolhido para manipular o forward do JstlView (retornado pelo InternalResourceViewResolver ). Em quase todos os casos, o DispatcherServlet não será configurado para lidar com tal solicitação .

Em vez disso, nesse caso simplista, você deve registrar o DispatcherServlet em / , marcando-o como o servlet padrão. O servlet padrão é a última correspondência para uma solicitação. Isso permitirá que seu contêiner de servlet típico escolha uma implementação de Servlet interna, mapeada para *.jsp , para manipular o recurso JSP (por exemplo, o Tomcat tem JspServlet ), antes de tentar com o servlet padrão.

Isso é o que você está vendo no seu exemplo.

Eu resolvi meu problema quando além de descrito antes: `

 @Bean public InternalResourceViewResolver resolver() { InternalResourceViewResolver vr = new InternalResourceViewResolver(); vr.setPrefix("/WEB-INF/jsps/"); vr.setSuffix(".jsp"); return vr; } 

added tomcat-embed-jasper:

  org.apache.tomcat.embed tomcat-embed-jasper provided  

`de: Arquivo JSP não sendo renderizado no aplicativo da Web Spring Boot

Eu me deparei com outro motivo para o mesmo erro. Isso também pode ser devido aos arquivos de class não gerados para o arquivo controller.java. Como resultado, o servlet do dispatcher mencionado em web.xml não consegue mapeá-lo para o método apropriado na class do controlador.

 @Controller Class Controller{ @RequestMapping(value="/abc.html")//abc is the requesting page public void method() {.....} } 

No eclipse em Projeto-> selecione Limpar -> Construir Projeto. Dê uma verificação se o arquivo de class foi gerado para o arquivo do controlador em construções em seu espaço de trabalho.