web-dev-qa-db-fra.com

Spring Boot ne servant pas de contenu statique

Je frappe ma tête contre le mur depuis quelques heures maintenant. Mon projet est presque terminé, mais je ne parviens pas à le diffuser en contenu statique.

J'ai placé un dossier nommé static sous src/main/resources. À l'intérieur, j'ai un dossier nommé images. Lorsque je compresse et exécute l'application, elle ne trouve pas les images que j'ai placées dans ce dossier.

J'ai essayé de mettre les fichiers statiques dans public, resources et META-INF/resources mais rien ne fonctionne.

Si je jar -tvf app.jar, je vois que les fichiers se trouvent dans le jar du dossier de droite: /static/images/head.png par exemple, mais en appelant: http://localhost:8080/images/head.png, tout ce que je reçois est un 404

Des idées pourquoi Spring-Boot ne trouve pas cela? (J'utilise 1.1.4 BTW)

124
Vinicius Carvalho

Ne pas ressusciter les morts après plus d'un an, mais toutes les réponses précédentes manquent quelques points cruciaux:

  1. @EnableWebMvc sur votre classe désactivera org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration. C'est bien si vous voulez un contrôle complet, mais sinon, c'est un problème.
  2. Il n'est pas nécessaire d'écrire du code pour ajouter un autre emplacement pour les ressources statiques en plus de ce qui est déjà fourni. En regardant org.springframework.boot.autoconfigure.web.ResourceProperties de v1.3.0.RELEASE, je vois un champ staticLocations qui peut être configuré dans le application.properties. Voici un extrait de la source:

    /**
     * Locations of static resources. Defaults to classpath:[/META-INF/resources/,
     * /resources/, /static/, /public/] plus context:/ (the root of the servlet context).
     */
    private String[] staticLocations = RESOURCE_LOCATIONS;
    
  3. Comme mentionné précédemment, l'URL de la demande sera résolue relative à ces emplacements. Ainsi, src/main/resources/static/index.html sera servi lorsque l'URL de la demande est /index.html. La classe chargée de résoudre le chemin, à partir de Spring 4.1, est org.springframework.web.servlet.resource.PathResourceResolver.

  4. La correspondance de modèle de suffixe est activée par défaut, ce qui signifie que, pour une URL de requête, /index.html, Spring cherchera les gestionnaires correspondant à /index.html. C'est un problème si l'intention est de servir du contenu statique. Pour le désactiver, étendez WebMvcConfigurerAdapter (mais n'utilisez pas @EnableWebMvc) et remplacez configurePathMatch comme indiqué ci-dessous:

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        super.configurePathMatch(configurer);
    
        configurer.setUseSuffixPatternMatch(false);
    }
    

IMHO, le seul moyen d'avoir moins de bugs dans votre code est de ne pas écrire du code quand c'est possible. Utilisez ce qui est déjà fourni, même si cela nécessite quelques recherches, le retour en vaut la peine.

134
Abhijit Sarkar

Contrairement à ce qu'indique l'état de spring-boot, pour que mon jar spring-boot serve le contenu:

@Configuration
public class StaticResourceConfiguration extends WebMvcConfigurerAdapter {

    private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
            "classpath:/META-INF/resources/", "classpath:/resources/",
            "classpath:/static/", "classpath:/public/" };

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
            .addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS);
    }
}
62
Francois

J'ai eu un problème similaire, et il s'est avéré que la solution simple consistait à faire étendre ma classe de configuration WebMvcAutoConfiguration:

@Configuration
@EnableWebMvc
@ComponentScan
public class ServerConfiguration extends WebMvcAutoConfiguration{
}

Je n'avais besoin d'aucun autre code pour autoriser la diffusion de mon contenu statique. Cependant, j'ai placé un répertoire appelé public sous src/main/webapp et configuré maven pour qu'il pointe vers src/main/webapp en tant que répertoire de ressources. Cela signifie que public est copié dans target/classes et se trouve donc dans le chemin d'accès aux classes à la recherche de Spring-Boot/Tomcat.

47
Engineer Dollery

Recherchez les contrôleurs mappés sur "/" ou sans chemin mappé.

J'ai eu un problème comme celui-ci, j'ai eu 405 erreurs et je me suis cogné la tête pendant des jours. Le problème s'est avéré être un contrôleur annoté @RestController que j'avais oublié d'annoter avec une annotation @RequestMapping. Je suppose que ce chemin mappé a pour valeur "/" et a bloqué le mappage des ressources de contenu statique.

21
Johannes

La configuration pourrait être faite comme suit:

@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcAutoConfigurationAdapter {

// specific project configuration

}

Il est important de noter que votre méthode WebMvcConfig may override addResourceHandlers et que vous devez par conséquent appeler explicitement super.addResourceHandlers(registry) (il est vrai que si vous êtes satisfait des emplacements de ressources par défaut, vous n'avez pas besoin de remplacer une méthode). 

Une autre chose à commenter ici est que ces emplacements de ressources par défaut (/static, /public, /resources et /META-INF/resources) ne seront enregistrés que s’il n’ya pas encore de gestionnaire de ressources mappé sur /**.

À partir de ce moment, si vous avez une image sur src/main/resources/static/images nommée image.jpg par exemple, vous pouvez y accéder en utilisant l'URL suivante: http://localhost:8080/images/image.jpg (serveur démarré sur le port 8080 et application déployée au contexte racine).

18
Francisco Spaeth

Avez-vous vérifié les les documents de référence Spring Boot

Par défaut, Spring Boot servira le contenu statique d'un dossier appelé /static (ou /public ou /resources ou /META-INF/resources) dans le chemin d'accès aux classes ou à la racine du ServletContext. 

Vous pouvez également comparer votre projet avec le guide Serving Web Content avec Spring MVC , ou consulter le code source du projet spring-boot-sample-web-ui .

11
matsev

J'avais ce problème exact, puis j'ai réalisé que j'avais défini dans mon application.properties: 

spring.resources.static-locations=file:/var/www/static

Ce qui dépassait tout ce que j'avais essayé. Dans mon cas, je voulais garder les deux, alors j'ai juste gardé la propriété et ajouté:

spring.resources.static-locations=file:/var/www/static,classpath:static

Qui a servi des fichiers de src/main/resources/static en tant qu’hôte local: {port} /file.html.

Tout ce qui précède n'a pas fonctionné pour moi car personne n'a mentionné cette petite propriété qui aurait pu être facilement copiée depuis Internet pour servir un but différent;)

J'espère que ça aide! Je pensais que cela ferait bien dans ce long post de réponses pour les personnes ayant ce problème.

5
S. Jacob Powell

Je pense que les réponses précédentes abordent très bien le sujet. Toutefois, j’ajouterais que, dans un cas où Spring Security est activé dans votre application, vous devrez peut-être spécifiquement indiquer à Spring d’autoriser les requêtes vers d’autres répertoires de ressources statiques, par exemple "/ static/fonts" .

Dans mon cas, "/ static/css", "/ static/js", "/ static/images" était autorisé par défaut, mais/static/fonts/** a été bloqué par mon implémentation de Spring Security.

Vous trouverez ci-dessous un exemple de la façon dont j'ai résolu ce problème.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
.....
    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/", "/fonts/**").permitAll().
        //other security configuration rules
    }
.....
}
5
RemusS

Cette solution fonctionne pour moi:

Tout d’abord, placez un dossier de ressources sous webapp/WEB-INF, comme suit:

-- src
  -- main
    -- webapp
      -- WEB-INF
        -- resources
          -- css
          -- image
          -- js
          -- ...

Deuxièmement, au printemps fichier de configuration

@Configuration
@EnableWebMvc
public class MvcConfig extends WebMvcConfigurerAdapter{

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

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

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resource/**").addResourceLocations("WEB-INF/resources/");
    }
}

Ensuite, vous pouvez accéder au contenu de vos ressources, tel que http: // localhost: 8080/resource/image/yourimage.jpg

3
ldeng

Juste pour ajouter encore une réponse à une vieille question ... Des gens ont mentionné que le @EnableWebMvc empêchera le chargement de WebMvcAutoConfiguration, qui est le code responsable de la création des gestionnaires de ressources statiques. Il existe d’autres conditions qui empêcheront le chargement de WebMvcAutoConfiguration. Le moyen le plus clair de voir cela est de regarder la source:

https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/Java/org/springframework/boot/autoconfigure/web servlet/WebMvcAutoConfiguration.Java # L139-L141

Dans mon cas, j'incluais une bibliothèque dont la classe s'étendait de WebMvcConfigurationSupport, condition qui empêchera la configuration automatique: 

@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)

Il est important de ne jamais prolonger de WebMvcConfigurationSupport. Au lieu de cela, étendre de WebMvcConfigurerAdapter.

2
Bal

2 points à prendre en compte (Spring Boot v1.5.2.RELEASE) - 1) Vérifiez l’annotation @EnableWebMvc dans toutes les classes de contrôleurs, supprimez-la s’il en existe. 2. Vérifiez les classes de contrôleurs pour lesquelles une annotation est utilisée - @RestController ou @Controller. Ne mélangez pas les comportements Rest API et MVC dans une classe. Pour MVC, utilisez @Controller et pour REST API, utilisez @RestController.

Faire au-dessus de 2 choses a résolu mon problème. Maintenant, mon démarrage de printemps charge des ressources statiques sans aucun problème . @ Controller => load index.html => charge les fichiers statiques.

@Controller
public class WelcomeController {

    // inject via application.properties
    @Value("${welcome.message:Hello}")
    private String message = "Hello World";

    @RequestMapping("/")
    public String home(Map<String, Object> model) {
        model.put("message", this.message);
        return "index";
    }

}

index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>index</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />


    <link rel="stylesheet/less" th:href="@{/webapp/assets/theme.siberia.less}"/>

    <!-- The app's logic -->
    <script type="text/javascript" data-main="/webapp/app" th:src="@{/webapp/libs/require.js}"></script>
    <script type="text/javascript">
        require.config({
            paths: { text:"/webapp/libs/text" }
        });
    </script>



   <!-- Development only -->
     <script type="text/javascript" th:src="@{/webapp/libs/less.min.js}"></script>


</head>
<body>

</body>
</html>
2
gjp

Je faisais face au même problème dans la botte de printemps 2.1.3 disant que la ressource n’était pas trouvée 404. J’ai retiré ci-dessous de applicatiion.properties .

#spring.resources.add-mappings=true
#spring.resources.static-locations=classpath:static
#spring.mvc.static-path-pattern=/**,

Supprimé @enableWebMVC et supprimé tout remplacement de WebMvcConfigurer

// @ EnableWebMvc

Assurez-vous également que vous avez @EnableAutoConfiguration dans votre configuration.

Et mettez toutes les ressources statiques dans src/main/resources/static et cela a finalement fonctionné comme par magie.

1
Debadatta

Si le problème se pose lors du lancement de l'application à partir d'un IDE (c'est-à-dire à partir d'Eclipse ou d'IntelliJ Idea) et de Maven, la clé de la solution se trouve dans Spring-boot Getting Started documentation:

Si vous utilisez Maven, exécutez:

mvn package && Java -jar target/gs-spring-boot-0.1.0.jar

La partie importante de celle-ci consiste à ajouter l'objectif package à exécuter avant le démarrage effectif de l'application. (Idée: menu Run, Edit Configrations..., Add, puis sélectionnez Run Maven Goal et spécifiez l’objectif package dans le champ)

1
ppeterka

J'utilise 1.3.5 et héberge un ensemble de services REST via la mise en œuvre de Jersey. Cela a bien fonctionné jusqu'à ce que je décide d'ajouter quelques fichiers HTML + js . Aucune des réponses données sur ce forum ne m'a aidé. Cependant, quand j'ai ajouté la dépendance suivante dans mon pom.xml, tout le contenu de src/main/resources/static était enfin affiché via le navigateur:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<dependency>

Il semble que spring-web/spring-webmvc soit l’importante dépendance transitive qui permet à la configuration automatique du démarrage du printemps de s’activer.

0
LilleElefant

Je suis dans la même situation où mon application spring-boot (intégration) ne sert pas le contenu de dossiers statiques pour afficher l'interface utilisateur sur localhost: 8080. Frontend est développé dans angular4 donc utilisé ng build qui a généré des fichiers dans le chemin de sortie dir src/main/resources/static mais n’affiche aucun contenu. J'ai créé un contrôleur spécialement pour servir index.html, mais il semble que quelque chose ne tourne pas rond pour que spring-boot puisse comprendre les problèmes de routage angulaire et localhost: 8080 n'affiche que la chaîne renvoyée par la méthode de contrôleur "index.html" sur la page Web. Ci-dessous, index.html (j'ai modifié la balise de sélecteur par défaut dans corps pour que le composant de connexion soit celui que j'ai créé et mon composant angulaire principal pour l'interface utilisateur, mais ne fonctionne toujours pas, que ce soit avec app-root ou celui-ci):

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Hello Test App</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
  <app-login></app-login>
<script type="text/javascript" src="runtime.js"></script><script type="text/javascript" src="polyfills.js"></script><script type="text/javascript" src="styles.js"></script><script type="text/javascript" src="vendor.js"></script><script type="text/javascript" src="main.js"></script></body>
</html>

Code du contrôleur:

@RequestMapping("/")
public String userInterface() {
    return "index.html";
}

Je ne sais pas si cela compte, mais il s’agit d’un projet basé sur plusieurs versions développé dans IntellijIdea et la version de démarrage à ressort est -org.springframework.boot:spring-boot-starter-web:2.0.2.RELEASE.

@Vizcaino - Comme vous l'avez dit, il existe un truc simple: Intellijidea propose-t-il une option similaire pour créer un répertoire/dossier source? J'ai créé à partir du menu contextuel -> Nouveau -> Répertoire. Mais pas sûr que ce soit la même chose, se demander si ce serait la cause de mon problème?

0
user6087439

Il est parfois utile de vérifier si vous avez annulé les mappages globaux par un contrôleur de repos. Exemple d'erreur simple (kotlin):

@RestController("/foo")
class TrainingController {

    @PostMapping
    fun bazz(@RequestBody newBody: CommandDto): CommandDto = return commandDto

}

Dans le cas ci-dessus, vous obtiendrez lorsque vous demanderez des ressources statiques:

{
    title: "Method Not Allowed",
    status: 405,
    detail: "Request method 'GET' not supported",
    path: "/index.html"
}

La raison en est peut-être que vous vouliez mapper @PostMapping à /foo sans oublier l'annotation @RequestMapping au niveau @RestController. Dans ce cas, toutes les demandes sont mappées sur POST et vous ne recevrez pas de contenu statique dans ce cas.

0
Przemek Nowak

FYI: J'ai aussi remarqué que je peux gâcher une application de démarrage de printemps qui fonctionne parfaitement et l'empêcher de servir le contenu du dossier statique, si j'ajoute un contrôleur de mauvais repos comme celui-ci 

 @RestController
public class BadController {
    @RequestMapping(method= RequestMethod.POST)
    public String someMethod(@RequestParam(value="date", required=false)String dateString, Model model){
        return "foo";
    }
}

Dans cet exemple, après avoir ajouté le mauvais contrôleur au projet, lorsque le navigateur demande un fichier disponible dans un dossier statique, la réponse d'erreur est '405, méthode non autorisée'. 

Les chemins d’avis ne sont pas mappés dans l’exemple de mauvais contrôleur. 

0
Ganesh

Avait le même problème, en utilisant Gradle et Eclipse et passé des heures à essayer de le comprendre.

Aucun codage requis, l'astuce consiste à utiliser l'option de menu Nouveau-> Dossier source (PAS Nouveau -> Dossier) pour créer le dossier statique sous src/main/resources. Je ne sais pas pourquoi cela fonctionne, mais new -> le dossier source, puis j’ai nommé le dossier static (la boîte de dialogue du dossier source donne alors une erreur que vous devez vérifier: mettez à jour les filtres d’exclusion dans les autres dossiers source pour résoudre l’imbrication). Le mon nouveau dossier statique, j’ai ajouté index.html et cela fonctionne maintenant.

0
Vizcaino