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

90

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 SpringServletConfigestá

@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 @Controllerno pacotecom.example.controllers

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

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

http://localhost:8080/Example/home

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

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

Tenho um recurso JSP em /WEB-INF/jsps/index.jspque esperava que o Spring MVC usasse meu controlador para lidar com a solicitação e encaminhar para o JSP, então por que ele está respondendo com um 404?


Esta é uma postagem canônica para perguntas sobre esta mensagem de aviso.

Sotirios Delimanolis
fonte

Respostas:

99

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

O DispatcherServletolha para ele ApplicationContexte, se disponível, o ApplicationContextregistrado com um ContextLoaderListenerpara beans especiais, ele precisa configurar sua lógica de atendimento de solicitação. Esses beans são descritos na documentação .

Provavelmente o mais importante, feijão do tipo HandlerMappingmapa

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

O javadoc deHandlerMapping descreve melhor como as implementações devem se comportar.

O DispatcherServletencontra todos os beans desse tipo e os registra em alguma ordem (pode ser personalizado). Enquanto atende a uma solicitação, o DispatcherServletloop atravessa esses HandlerMappingobjetos e testa cada um deles getHandlerpara encontrar um que possa lidar com a solicitação recebida, representada como o padrão HttpServletRequest. A partir de 4.3.x, se não encontrar nenhum , ele registra o aviso que você vê

Nenhum mapeamento encontrado para solicitação HTTP com URI [/some/path] emDispatcherServlet com o nome SomeName

e qualquer um um lança umNoHandlerFoundException ou imediatamente compromete a resposta com um código de status 404 Not Found.

Por que não DispatcherServlet encontrou umHandlerMapping que pudesse atender ao meu pedido?

A HandlerMappingimplementação mais comum é RequestMappingHandlerMapping, que lida com o registro de @Controllerbeans como manipuladores (na verdade, seus @RequestMappingmétodos anotados). Você pode declarar um bean deste tipo sozinho (com @Beanou <bean>ou outro mecanismo) ou pode usar as opções integradas . Esses são:

  1. Anote o seu @Configuration classe com @EnableWebMvc.
  2. Declare um <mvc:annotation-driven /> membro em sua configuração XML.

Como o link acima descreve, ambos registrarão um RequestMappingHandlerMappingbean (e um monte de outras coisas). No entanto, um HandlerMappingnão é muito útil sem um manipulador. RequestMappingHandlerMappingespera alguns @Controllerbeans, então você precisa declará-los também, por meio de @Beanmétodos em uma configuração Java ou <bean>declarações em uma configuração XML ou por meio de varredura de componente de@Controller classes anotadas em qualquer um deles. Certifique-se de que esses grãos estejam presentes.

Se você está recebendo a mensagem de aviso e um 404 e configurou todos os itens acima corretamente, então você está enviando sua solicitação para o URI errado , um que não é tratado por um @RequestMappingmétodo de manipulador anotado detectado .

A spring-webmvcbiblioteca oferece outras HandlerMappingimplementações integradas . Por exemplo,BeanNameUrlHandlerMapping mapas

de URLs a beans com nomes que começam com uma barra ("/")

e você sempre pode escrever o seu próprio. Obviamente, você terá que garantir que a solicitação que está enviando corresponda a pelo menos um dosHandlerMapping manipuladores do objeto .

Se você não registrar implícita ou explicitamente nenhum HandlerMappingbean (ou se detectAllHandlerMappingsestiver true), o DispatcherServletregistrará alguns padrões . Eles são definidos no DispatcherServlet.propertiesmesmo pacote da DispatcherServletclasse. Eles são BeanNameUrlHandlerMappinge DefaultAnnotationHandlerMapping(que é semelhante a, RequestMappingHandlerMappingmas obsoleto).

Depurando

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

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

irá registrar o seguinte no nível de INFO

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

Isso descreve o mapeamento registrado. Ao ver 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 para que o Spring MVC selecione o manipulador.

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

Da mesma forma, habilite o registro do Spring no nível DEBUG para ver quais beans o Spring registra. Ele deve relatar quais classes anotadas ele encontra, quais pacotes ele verifica e quais beans ele inicializa. Se os que você esperava não estiverem presentes, revise sua ApplicationContextconfiguração.

Outros erros comuns

A DispatcherServleté apenas um Java EE típico Servlet. Você o registra com sua declaração típica <web.xml> <servlet-class>e <servlet-mapping>, ou diretamente por meio ServletContext#addServletde um WebApplicationInitializer, ou com qualquer mecanismo que o Spring boot usa. Como tal, você deve confiar na lógica de mapeamento de url especificada na especificação do Servlet , consulte o Capítulo 12. Consulte também

Com isso em mente, um erro comum é registrar o DispatcherServletcom um mapeamento de url de /*, retornar um nome de visualização de um @RequestMappingmétodo manipulador e esperar que um JSP seja renderizado. Por exemplo, considere um método 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 a solicitação seja encaminhada 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, oDisaptcherServlet relatório

Nenhum mapeamento encontrados para solicitação HTTP com URI [/Example/WEB-INF/jsps/example-view-name.jsp]no DispatcherServletcom o nome 'expedidor'

Como o DispatcherServleté mapeado para /*e /*corresponde a tudo (exceto correspondências exatas, que têm maior prioridade), o DispatcherServletseria escolhido para lidar com o forwarddo JstlView(retornado pelo InternalResourceViewResolver). Em quase todos os casos, o DispatcherServletnão será configurado para lidar com essa solicitação .

Em vez disso, neste caso simplista, você deve registrar o DispatcherServletpara /, 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 Servlet interna, mapeada para *.jsp, para lidar com o recurso JSP (por exemplo, Tomcat temJspServlet ), antes de tentar com o servlet padrão.

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

Sotirios Delimanolis
fonte
Com @EnableWebMvc, o dispatcherServlet já está registrado em /. "você pode esperar que a solicitação seja encaminhada para um recurso JSP no caminho /WEB-INF/jsps/example-view-name.jsp. Isso não acontecerá." Como você faz isso funcionar para que seja encaminhado para um recurso JSP nesse caminho? Essa é basicamente a pergunta feita.
Tor
@Tor sozinho, @EnableWebMvcem uma @Configurationclasse anotada não faz isso. Tudo o que ele faz é adicionar uma série de manipuladores / adaptadores Spring MVC padrão ao contexto do aplicativo. O registro de um DispatcherServletpara servir /é um processo completamente separado que é feito de várias maneiras que descrevo na seção Outros erros comuns . Eu respondo à pergunta feita dois parágrafos abaixo do que você citou.
Sotirios Delimanolis
5

Resolvi meu problema além do descrito antes: `

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

added tomcat-embed-jasper:

<dependency>
       <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
       <scope>provided</scope>
</dependency>

`from: Arquivo JSP não renderizado no aplicativo da web Spring Boot

RoutesMaps.com
fonte
2

No meu caso, eu estava seguindo a documentação do Interceptors Spring para a versão 5.1.2 (enquanto usava Spring Boot v2.0.4.RELEASE ) e a WebConfigclasse tinha a anotação @EnableWebMvc, que parecia estar em conflito com outra coisa em meu aplicativo que estava impedindo minha estática ativos sejam resolvidos corretamente (ou seja, nenhum arquivo CSS ou JS estava sendo devolvido ao cliente).

Depois de tentar várias coisas diferentes, tentei remover o @EnableWebMvce funcionou!

Editar: aqui está a documentação de referência que diz que você deve remover a @EnableWebMvcanotação

Aparentemente, pelo menos no meu caso, já estou configurando meu aplicativo Spring (embora não usando web.xmlou qualquer outro arquivo estático, é definitivamente programaticamente), então era um conflito aí.

Acapulco
fonte
1

Tente corrigir seu código com a seguinte alteração em seu arquivo de configuração. A configuração Java é usada em vez de application.properties. Não se esqueça de habilitar a configuração no configureDefaultServletHandlingmétodo.

WebMvcConfigurerAdapterclasse está obsoleta, então usamos WebMvcConfigurerinterface.

@Configuration
@EnableWebMvc
@ComponentScan
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/views/", ".jsp");
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

Eu uso o Gradle, você deve ter as seguintes dependências em pom.xml:

dependencies {

    compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.3.0.RELEASE'
    compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '9.0.35'
}
Marca
fonte
0

Encontrei outro motivo para o mesmo erro. Isso também pode ser devido aos arquivos de classe não gerados para seu arquivo controller.java. Como resultado, o servlet do dispatcher mencionado em web.xml não consegue mapeá-lo para o método apropriado na classe 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. Verifique se o arquivo de classe foi gerado para o arquivo do controlador em construções em sua área de trabalho.

Anil NP
fonte
0

Para mim, descobri que minhas classes de destino foram geradas em um padrão de pasta diferente da origem. Isso é possivelmente no eclipse, eu adiciono pastas para conter meus controladores e não os adiciono como pacotes. Então acabei definindo o caminho incorreto na configuração do Spring.

Minha classe de destino estava gerando classes em app e eu estava me referindo a com.happy.app

<context:annotation-config />
<context:component-scan
    base-package="com.happy.app"></context:component-scan> 

Eu adicionei pacotes (não pastas) para com.happy.app e movi os arquivos de pastas para pacotes no eclipse e isso resolveu o problema.

Roy
fonte
0

Limpe seu servidor. Talvez exclua o servidor e adicione o projeto novamente e execute.

  1. Pare o servidor Tomcat

  2. Clique com o botão direito no servidor e selecione "Limpar"

  3. Clique com o botão direito do mouse novamente no servidor e selecione "Limpar diretório de trabalho do Tomcat"

2rahulsk
fonte
0

No meu caso, eu estava brincando com a importação de arquivos de configuração java secundários para um arquivo de configuração java principal. Ao criar arquivos de configuração secundários, alterei o nome da classe de configuração principal, mas não consegui atualizar o nome em web.xml. Portanto, sempre que reiniciei meu servidor tomcat, não estava vendo os manipuladores de mapeamento observados no console do Eclipse IDE e, quando tentei navegar para minha página inicial, encontrei este erro:

1 de novembro de 2019 11:00:01 org.springframework.web.servlet.PageNotFound noHandlerFound AVISO: Nenhum mapeamento encontrado para solicitação HTTP com URI [/ webapp / home / index] em DispatcherServlet com o nome 'dispatcher'

A correção foi atualizar o arquivo web.xml para que o nome antigo "WebConfig" fosse "MainConfig", simplesmente renomeando-o para refletir o nome mais recente do arquivo de configuração java principal (onde "MainConfig" é arbitrário e as palavras " Web "e" Principal "usados ​​aqui não são um requisito de sintaxe). MainConfig foi importante, porque foi o arquivo que fez a varredura de componente para "WebController", minha classe de controlador Spring mvc que lida com minhas solicitações da web.

@ComponentScan(basePackageClasses={WebController.class})

web.xml tinha isto:

<init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        com.lionheart.fourthed.config.WebConfig
    </param-value>
</init-param>

O arquivo web.xml agora tem:

<init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        com.lionheart.fourthed.config.MainConfig
    </param-value>
</init-param>

Agora estou vendo o mapeamento na janela do console:

INFO: Mapeado "{[/ home / index], methods = [GET]}" para public org.springframework.web.servlet.ModelAndView com.lionheart.fourthed.controller.WebController.gotoIndex ()

E minha página da web está carregando novamente.

Steve T
fonte
-1

Eu tive o mesmo problema que **No mapping found for HTTP request with URI [/some/path] in DispatcherServlet with name SomeName**

Depois de analisar por 2 a 4 dias, descobri a causa raiz. Os arquivos de classe não foram gerados depois de executar o projeto. Cliquei na guia do projeto.

Projeto -> Fechar Projeto -> OpenProject -> Limpar -> Construir projeto

Arquivos de classe para código-fonte foram gerados. Isso resolveu meu problema. Para verificar se os arquivos de classe foram gerados ou não, verifique a pasta Build na pasta do seu projeto.

Thanis Albert
fonte