49 votes

Springboot/Angular2 - Comment gérer les urls HTML5 ?

Je pense qu'il s'agit d'une question simple, mais je n'ai pas pu trouver de réponse ou du moins utiliser les bons termes dans la recherche.

Je suis en train de mettre en place Angular2 et Springboot ensemble. Par défaut, Angular utilisera des chemins comme localhost:8080\dashboard et localhost:8080\dashboard\detail .

J'aimerais éviter d'utiliser les chemins comme des hashs, si possible. Comme Angular documentation États :

La fonction provideRouter du routeur définit la LocationStrategy sur la PathLocationStrategy, ce qui en fait la stratégie par défaut. Nous pouvons passer à la stratégie HashLocationStrategy avec une commande prioritaire pendant le processus de démarrage si nous le préférons.

Et puis...

Presque tous les projets Angular 2 devraient utiliser le style HTML 5 par défaut. Il produit des URL plus faciles à comprendre pour les utilisateurs. Et cela permet de conserver la possibilité d'effectuer un rendu côté serveur ultérieurement.

Le problème est que lorsque j'essaie d'accéder localhost:8080\dashboard Spring cherchera un contrôleur correspondant à ce chemin, ce qui n'est pas le cas.

Whitelabel Error Page
There was an unexpected error (type=Not Found, status=404).
No message available

J'ai pensé initialement à faire en sorte que tous mes services soient sous localhost:8080\api et toutes mes statiques sous localhost:8080\app . Mais comment puis-je dire à Spring d'ignorer les requêtes de ce app chemin ?

Existe-t-il une meilleure solution avec Angular2 ou Boot ?

0 votes

Votre route angulaire devrait ressembler à localhost:8080\#dashboard et localhost:8080\#dashboard \detail

0 votes

Salut @tashi, j'aimerais éviter d'utiliser des hachages si possible... J'ai mis à jour le sujet pour refléter cela.. Je n'ai pas été assez clair la première fois...

0 votes

Non, utilisez simplement le style html

60voto

davidh Points 107

Dans mes applications Spring Boot (version 1 et 2), mes ressources statiques se trouvent à un seul endroit :

src/main/resources/static

static étant un dossier reconnu par Spring Boot pour charger les ressources statiques.

L'idée est ensuite de personnaliser la configuration de Spring MVC.
La méthode la plus simple consiste à utiliser la configuration de Spring Java.

Je mets en œuvre WebMvcConfigurer pour passer outre addResourceHandlers() . J'ajoute dans un simple ResourceHandler à l'actuel ResourceHandlerRegistry .
Le gestionnaire est mappé sur chaque requête et je spécifie classpath:/static/ comme valeur d'emplacement des ressources (vous pouvez bien sûr en ajouter d'autres si nécessaire).
J'ajoute un PathResourceResolver classe anonyme à surcharger getResource(String resourcePath, Resource location) .
Et la règle pour retourner la ressource est la suivante : si la ressource existe et est lisible (donc c'est un fichier), je la retourne. Sinon, par défaut, je retourne le fichier index.html page. Ce qui est le comportement attendu pour gérer les urls HTML 5.

Application Spring Boot 1.X :

Extension du site org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter est le chemin.
Cette classe est un adaptateur de la WebMvcConfigurer interface avec des méthodes vides permettant aux sous-classes de ne surcharger que les méthodes qui les intéressent.

Voici le code complet :

import java.io.IOException;

import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.resource.PathResourceResolver;

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

    registry.addResourceHandler("/**/*")
        .addResourceLocations("classpath:/static/")
        .resourceChain(true)
        .addResolver(new PathResourceResolver() {
            @Override
            protected Resource getResource(String resourcePath,
                Resource location) throws IOException {
                  Resource requestedResource = location.createRelative(resourcePath);
                  return requestedResource.exists() && requestedResource.isReadable() ? requestedResource
                : new ClassPathResource("/static/index.html");
            }
        });
    }
}

Application Spring Boot 2.X :

org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter a été déprécié.
Mise en œuvre directe WebMvcConfigurer est la solution actuelle, car il s'agit toujours d'une interface, mais elle possède désormais des méthodes par défaut (rendues possibles par une ligne de base de Java 8) et peut être implémentée directement sans avoir besoin de l'adaptateur.

Voici le code complet :

import java.io.IOException;

import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.PathResourceResolver;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

      registry.addResourceHandler("/**/*")
        .addResourceLocations("classpath:/static/")
        .resourceChain(true)
        .addResolver(new PathResourceResolver() {
            @Override
            protected Resource getResource(String resourcePath,
                Resource location) throws IOException {
                Resource requestedResource = location.createRelative(resourcePath);
                return requestedResource.exists() && requestedResource.isReadable() ? requestedResource
                : new ClassPathResource("/static/index.html");
            }
        });
    }
}

0 votes

Devrait WebMvcConfig étend WebMvcConfigurerAdapter au lieu de mettre en œuvre WebMvcConfigurer puisqu'il s'agit d'une interface ?

1 votes

Si vous utilisez Spring Boot 1, oui vous devez utiliser WebMvcConfigurerAdapter . Mais dans Spring Boot 2, il a été déprécié, WebMvcConfigurer est toujours une interface mais elle dispose désormais de méthodes par défaut (rendues possibles par une baseline Java 8) et peut être implémentée directement sans avoir besoin de l'adaptateur.

0 votes

J'ai mis à jour pour faire une distinction claire selon la version.

56voto

AndroidLover Points 598

J'ai une solution pour vous, vous pouvez ajouter une ViewController pour transmettre les requêtes à Angular depuis Spring boot.

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class ViewController {

@RequestMapping({ "/bikes", "/milages", "/gallery", "/tracks", "/tracks/{id:\\w+}", "/location", "/about", "/tests","/tests/new","/tests/**","/questions","/answers" })
   public String index() {
       return "forward:/index.html";
   }
}

ici j'ai redirigé tous mes angular2 ("/bikes", "/milages", "/gallery", "/tracks", "/tracks/{id : \w +}", "/location", "/about", "/tests", "/tests/new", "/tests/**", "/questions", "/answers") vers mon SPA Vous pouvez faire la même chose pour votre projet et vous pouvez également rediriger votre page d'erreur 404 vers la page d'index comme une étape supplémentaire. Bonne lecture !

0 votes

Si j'utilise cette méthode, j'obtiens toujours un rafraîchissement complet de la page :(

0 votes

@Hans non vous ne devriez pas avoir un rafraîchissement complet de la page vous avez un autre problème

1 votes

@AndroidLover non tout va bien, j'ai seulement un rafraîchissement complet de la page si je recharge avec f5 ou si j'entre la nouvelle url. mais c'est comme ça que ça devrait être. je pensais mal...

16voto

Dmitry Serdiuk Points 73

Vous pouvez transférer toutes les ressources non trouvées vers votre page principale en fournissant un ErrorViewResolver personnalisé. Tout ce que vous avez à faire est d'ajouter ceci à votre classe @Configuration :

@Bean
ErrorViewResolver supportPathBasedLocationStrategyWithoutHashes() {
    return new ErrorViewResolver() {
        @Override
        public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
            return status == HttpStatus.NOT_FOUND
                    ? new ModelAndView("index.html", Collections.<String, Object>emptyMap(), HttpStatus.OK)
                    : null;
        }
    };
}

0 votes

Pour ajouter une explication, ErrorViewResolver est une interface qui doit être implémentée par votre classe ayant l'annotation @Configuration, à part cela, il s'agit d'une bonne solution dynamique, transférant la responsabilité de la gestion des erreurs à l'application Angular, fonctionnant à l'intérieur de l'application Spring Boot.

0 votes

Comme j'utilise Angular 6, j'ai dû utiliser "index" au lieu de "index.html".

0 votes

C'est la solution que j'ai finalement retenue. Il serait plus propre de retourner un HttpStatus de type redirect au lieu de OK cependant, cela a plus de sens sémantiquement.

10voto

Loren Points 57

Vous pouvez transférer tout ce qui n'est pas mappé à Angular en utilisant quelque chose comme ceci :

@Controller
public class ForwardController {

    @RequestMapping(value = "/**/{[path:[^\\.]*}")
    public String redirect() {
        // Forward to home page so that route is preserved.
        return "forward:/";
    }
} 

Source : https://stackoverflow.com/a/44850886/3854385

Mon serveur Spring Boot pour angular est également un serveur de passerelle avec les appels API à /api pour ne pas avoir une page de connexion devant les pages angulaires, vous pouvez utiliser quelque chose comme.

import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

/**
 * This sets up basic authentication for the microservice, it is here to prevent
 * massive screwups, many applications will require more secuity, some will require less
 */

@EnableOAuth2Sso
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .logout().logoutSuccessUrl("/").and()
                .authorizeRequests()
                .antMatchers("/api/**").authenticated()
                .anyRequest().permitAll().and()
                .csrf()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    }
}

0 votes

Dans mon cas, tout ce dont j'avais besoin était @RequestMapping(value = "/{ :[^] \\. ]*}") dans le ForwardController

3voto

Akhil Bojedla Points 234

Pour rendre les choses plus simples, vous pouvez simplement implémenter directement ErrorPageRegistrar

@Component
public class ErrorPageConfig implements ErrorPageRegistrar {

    @Override
    public void registerErrorPages(ErrorPageRegistry registry) {
        registry.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/"));
    }

}

Cela renverrait les demandes à index.html.

@Controller
@RequestMapping("/")
public class MainPageController {

    @ResponseStatus(HttpStatus.OK)
    @RequestMapping({ "/" })
    public String forward() {
        return "forward:/";
    }
}

0 votes

Cette solution rejette les exceptions

0 votes

S'il vous plaît, aidez-moi avec l'exception stacktrace

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X