Solucionado (ver solução)
Solucionado
(ver solução)
24
respostas

Os arquivos css e js o spring

Os arquivos css e js o spring não está conseguindo incluir no projeto.

Como a configuração está em java e não em xml, como resolver com que o jsp enxerga estes arquivos.

No console mostra este erro:

16:38:28.089 [http-nio-8080-exec-10] DEBUG org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'dispatcher' processing GET request for [/desif/resources/js/bootstrap.min.js] 16:38:28.089 [http-nio-8080-exec-10] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Looking up handler method for path /resources/js/bootstrap.min.js 16:38:28.090 [http-nio-8080-exec-10] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Did not find handler method for [/resources/js/bootstrap.min.js] 16:38:28.090 [http-nio-8080-exec-10] WARN org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/desif/resources/js/bootstrap.min.js] in DispatcherServlet with name 'dispatcher' 16:38:28.090 [http-nio-8080-exec-10] DEBUG org.springframework.web.servlet.DispatcherServlet - Successfully completed request

24 respostas

Fala Guilherme, tudo bem ?

Você pode liberar da seguinte maneira:

Classe de configuração da app web

public class WebAppConfiguration extends WebMvcConfigurerAdapter {

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

Assim quando a servlet do Spring não conseguir resolver as requisições para os estáticos, o Spring ainda delega o trabalho para o tomcat, que vai devolver os arquivos.

Se você estiver usando o Spring Security, pode ser que você ainda seja bloqueado. Neste caso, poderia fazer o seguinte:

Classe de configurações de segurança

@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
    ...
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**");
    }
    ...
}

Assim toda requisição pra sua pasta de recursos estáticos será ignorada por padrão pelo contexto de segurança.

Espero ter ajudado. Abraço!

Não funcionou.

Criei a classe:

public class WebAppConfiguration extends WebMvcConfigurerAdapter {

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

Aonde chamo esta classe ?

Nesta

package br.com.netsoft.desif;

import javax.servlet.Filter;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletRegistration.Dynamic;

import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class ServletSpringMVC extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { AppWebConfiguration.class, JPAConfiguration.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
        encodingFilter.setEncoding("UTF-8");
        return new Filter[] { encodingFilter };
    }

    @Override
    protected void customizeRegistration(Dynamic registration) {
        registration.setMultipartConfig(new MultipartConfigElement(""));
    }
}

Opa Guilherme,

Dei apenas um exemplo do código que você vai precisar na classe de configuração. Não precisa criar uma classe nova, basta fazer com que sua classe de configuração já existente AppWebConfiguration, que é passada no getServletConfigClasses(), contenha, além do código dela, mais esse código que tinha no meu exemplo.

Isso já deve resolver.

Abraço!

Entendi.

Tive que fazer algumas mudanças, por causa do Thymeleaf.

Código que você informou

package br.com.netsoft.desif;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("/webjars/").resourceChain(true);
    }
}

Inicialização do Spring

package br.com.netsoft.desif;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class SpringWebInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.scan(SpringWebInitializer.class.getPackage().getName());
        servletContext.addListener(new ContextLoaderListener(applicationContext));
        servletContext.addListener(new RequestContextListener());
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", dispatcherServlet(applicationContext));
        dispatcher.setAsyncSupported(true);
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    }

    private DispatcherServlet dispatcherServlet(WebApplicationContext applicationContext) {
        return new DispatcherServlet(applicationContext);
    }
}

Configuração do Thymeleaf

package br.com.netsoft.desif;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.thymeleaf.ITemplateEngine;
import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ITemplateResolver;

import nz.net.ultraq.thymeleaf.LayoutDialect;

@Configuration
public class ThymeleafConfig {

    @Autowired
    private ApplicationContext applicationContext;

    @Bean
    public ViewResolver thymeleafViewResolver(ITemplateEngine templateEngine) {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(templateEngine);
        viewResolver.setCharacterEncoding("UTF-8");
        return viewResolver;
    }

    @Bean
    public ITemplateEngine templateEngine(ITemplateResolver templateResolver) {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver);
        templateEngine.setEnableSpringELCompiler(true);
        templateEngine.addDialect(new LayoutDialect());
        templateEngine.addDialect(new SpringSecurityDialect());
        return templateEngine;
    }

    @Bean
    public ITemplateResolver templateResolver() {
        SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
        templateResolver.setApplicationContext(this.applicationContext);
        templateResolver.setTemplateMode(TemplateMode.HTML);
        templateResolver.setPrefix("/WEB-INF/views/");
        templateResolver.setSuffix(".html");
        templateResolver.setCharacterEncoding("UTF-8");
        templateResolver.setCacheable(false);
        return templateResolver;
    }
}

Mas não tenho idéia de como adicionar o que você está falando.

Como está o código da classe que AppWebConfiguration ? Que você passa nesse trecho:

@Override
protected Class<?>[] getServletConfigClasses() {
    return new Class[] { AppWebConfiguration.class, JPAConfiguration.class };
}

É só colocar aquela sobrescrita de método la da primeira resposta nessa classe aí, que deve resolver.. ficaria assim

@EnableWebMvc
...
public class AppWebConfiguration extends WebMvcConfigurerAdapter {

    // todo o código anterior de configuração com seus beans etc
    ...    
    @Override
    public void defaultServletHandling(DefaultServletHandlerConfigurer configurer) {
         configurer.enable();
    }
    ...
}

Aí não precisaria da classe WebConfig só com aquele código

Criei esta classe

package br.com.netsoft.desif;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
@EnableWebMvc
public class ServletSpringMVC extends WebMvcConfigurerAdapter {

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

No HTML

<!DOCTYPE html>
<html lang="pt-br"
    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<title>Países</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link
    href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
    rel="stylesheet"></link>
<script
    src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link href="resources/css/css.css" rel="stylesheet" />
</head>

Não acha o arquivo css

Guilherme, não precisa criar nenhuma classe nova. Só precisa colocar o extends WebMvcConfigurerAdapter na sua classe (que já existia) chamada AppWebConfiguration. E a partir daí adicionar a sobrescrita daquele método citado entre os métodos que ela já tem.

Poste o código da classe AppWebConfiguration (não essa nova, que inclusive pode ser removida) aqui pra poder dar uma olhada.

Entendi Rafael.

Está assim

package br.com.netsoft.desif;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.format.datetime.DateFormatterRegistrar;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

import br.com.netsoft.desif.controller.HomeController;

@EnableWebMvc
@ComponentScan(basePackageClasses = { HomeController.class })
//@Configuration
public class AppWebConfiguration extends WebMvcConfigurerAdapter {

    @Bean
    public InternalResourceViewResolver internalResourceViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".html");
        return resolver;
    }

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("/WEB-INF/messages");
        messageSource.setDefaultEncoding("UTF-8");
        messageSource.setCacheSeconds(1);
        return messageSource;
    }

    @Bean
    public FormattingConversionService mvcConversionService() {
        DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
        DateFormatterRegistrar registrar = new DateFormatterRegistrar();
        registrar.setFormatter(new DateFormatter("dd/MM/yyyy"));
        registrar.registerFormatters(conversionService);
        return conversionService;
    }

    @Bean
    public MultipartResolver multipartResolver() {
        return new StandardServletMultipartResolver();
    }

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

O que falta ?

Oi Guilherme,

Aparentemente o código dessa classe está ok. Não consigo ver o problema.

Esse código está em algum repositório (github, por exemplo) pra poder baixar e testar localmente ?

Qual é o correto ?

O vermelho ou o preto ?

Qual endereço devo chamar o arquivo css ?

Fala Guilherme,

Vermelho! Mas tem um porém. A pasta WEB-INF só é visível de dentro do servidor (garantido pela espec), portanto, quando o browser tentar requisitar o css ele não vai conseguir enxergar independentemente de configuração.

A pasta /resources deve estar em /webapp, e não em /WEB-INF

No HTML coloquei assim

<link src="/desif/resources/css/css.css" rel="stylesheet"></link>

Mas não funcionou.

Abrindo o browser com estes endereços abaixo não funciona:

1) http://localhost:8080/desif/css/css.css 2) http://localhost:8080/desif/css.css 3) http://localhost:8080/desif/resources/css/css.css

Agora está assim:

Está correto ?

No console sempre mostra

16:35:24.639 [http-nio-8080-exec-14] DEBUG org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'dispatcher' processing GET request for [/desif/resources/css/css.css]
16:35:24.639 [http-nio-8080-exec-14] WARN org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/desif/resources/css/css.css] in DispatcherServlet with name 'dispatcher'
16:35:24.639 [http-nio-8080-exec-14] DEBUG org.springframework.web.servlet.DispatcherServlet - Successfully completed request

Fala Guilherme,

Deveria funcionar esse caminho /desif/resources/css/css.css. Tente acessar no navegador http://localhost:8080/desif/resources/css/css.css dê uma olhada no que aparece. O conteúdo dentro de /webapp fica disponível para o cliente navegador, somado a aquela configuração anterior (do spring), deveria funcionar.

Tem como dar uma olhada no código, tem algum repositório ? É complicado achar o erro só por aqui, sem dar uma olhada e testar.

Alternativamente você pode acompanhar o exemplo do curso, onde é carregado os css, e com tudo funcionando migrar seu código pra dentro do projeto configurado bonitinho.

Abraço!

Aparece isto no console

18:01:16.738 [http-nio-8080-exec-6] DEBUG org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'dispatcher' processing GET request for [/desif/resources/css/css.css]
18:01:16.738 [http-nio-8080-exec-6] WARN org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/desif/resources/css/css.css] in DispatcherServlet with name 'dispatcher'
18:01:16.738 [http-nio-8080-exec-6] DEBUG org.springframework.web.servlet.DispatcherServlet - Successfully completed request

No navegador dá 404

Tem exemplo de spring, Thymeleaf e postgres ?

É Guilherme, teria que analisar melhor o codigo, o projeto em si, pra ver o por que não devolve a página.

No curso de Spring Boot é usado Thymeleaf, mas o conceito que precisamos pra resolver é exatamente o mesmo do curso de Spring MVC que usa JSP. Se você seguir o passo de configuração do curso, pouco importa o template de view que está usando (JSP ou Thymeleaf) ou o banco de dados utilizado, já que o projeto utiliza JPA abstraindo o trabalho com a base de dados.

OK. Aonde consigo o código deste curso ?

Acesse o curso de Spring Boot. Em cada aula temos no último step o projeto com o conteúdo final pra você poder baixar.

Consegui, mas mudando algumas configurações

WebConfig

package br.com.netsoft.desif;

import java.math.BigDecimal;
import java.util.Locale;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.number.NumberStyleFormatter;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.i18n.FixedLocaleResolver;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ITemplateResolver;

import br.com.netsoft.desif.controller.ProController;
import nz.net.ultraq.thymeleaf.LayoutDialect;

@Configuration
@ComponentScan(basePackageClasses = { ProController.class })
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Bean
    public ViewResolver viewResolver() {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine());
        resolver.setCharacterEncoding("UTF-8");
        return resolver;
    }

    @Bean
    public TemplateEngine templateEngine() {
        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.setEnableSpringELCompiler(true);
        engine.setTemplateResolver(templateResolver());

        engine.addDialect(new LayoutDialect());
        // engine.addDialect(new BrewerDialect());
        return engine;
    }

    private ITemplateResolver templateResolver() {
        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
        resolver.setApplicationContext(applicationContext);
        resolver.setPrefix("classpath:/templates/");
        resolver.setCharacterEncoding("UTF-8");
        resolver.setSuffix(".html");
        // resolver.setTemplateMode(TemplateMode.HTML);
        return resolver;
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
    }

    @Bean
    public FormattingConversionService mvcConversionService() {
        DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
        // conversionService.addConverter(new EstiloConverter());

        NumberStyleFormatter bigDecimalFormatter = new NumberStyleFormatter("#,##0.00");
        conversionService.addFormatterForFieldType(BigDecimal.class, bigDecimalFormatter);

        NumberStyleFormatter integerFormatter = new NumberStyleFormatter("#,##0");
        conversionService.addFormatterForFieldType(Integer.class, integerFormatter);

        return conversionService;
    }

    @Bean
    public LocaleResolver localeResolver() {
        return new FixedLocaleResolver(new Locale("pt", "BR"));
    }

}

AppInitializer

package br.com.netsoft.desif;

import javax.servlet.Filter;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletRegistration.Dynamic;

import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { JPAConfig.class, ServiceConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { WebConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        characterEncodingFilter.setForceEncoding(true);

        return new Filter[] { characterEncodingFilter };
    }

    @Override
    protected void customizeRegistration(Dynamic registration) {
        registration.setMultipartConfig(new MultipartConfigElement(""));
    }

}
solução!

Mas para mostrar o html, tive que mudar os arquivos html de "/src/main/webapp/WEB-INF/" para "/src/main/resources/templates/".

Conforme imagem:

Tem diferença ?

Se sim qual ?

Fala Guilherme,

As pastas static e template são pastas convencionadas pelo Spring Boot pra já expor os arquivos pro contexto web. Opcionalmente podemos fazer aquelas configurações mencionadas lá em cima da forma tradicional com Spring MVC, mantendo em webapp/resources/** e as views em WEB-INF/views/.

Abraço!

Então. Mas questão de segurança, contexto, mercado qual é o mais usual ?

Se altero as congurações

De

SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
    resolver.setApplicationContext(applicationContext);
        resolver.setPrefix("classpath:/templates/");
        resolver.setCharacterEncoding("UTF-8");
        resolver.setSuffix(".html");

Para:

private ITemplateResolver templateResolver() {
        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
    resolver.setApplicationContext(applicationContext);
        resolver.setPrefix("/WEB-INF/paginas/");
        resolver.setCharacterEncoding("UTF-8");
        resolver.setSuffix(".html");
        return resolver;
    }

e

De

@Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
    }

Para:

@Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("/webjars/").resourceChain(true);
    }

O sistema não consegue ver os arquivos css e js que estão no contexto: resources, entendeu ?

Estou querendo inserir o springSecurity, tem diferença ?