web-dev-qa-db-fra.com

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

Je pense que la question est simple, mais je n’ai pas pu trouver de réponse ni au moins utiliser les termes corrects dans la recherche.

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

J'aimerais éviter d'utiliser path comme hash, si possible. Comme angulaire documentation déclare:

La fonction supplyRouter du routeur définit la LocationStrategy sur la PathLocationStrategy, ce qui en fait la stratégie par défaut. Nous pouvons basculer vers HashLocationStrategy avec une substitution pendant le processus d’amorçage si nous le préférons.

Et alors...

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

Le problème est que lorsque j'essaie d'accéder à localhost:8080\dashboard, Spring cherchera un mappage de contrôleur sur ce chemin, ce qu'il n'aura pas.

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

Au départ, je pensais que tous mes services étaient sous localhost:8080\api et que tous mes statiques étaient sous localhost:8080\app. Mais comment puis-je dire à Spring d'ignorer les requêtes sur ce chemin app?

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

32
Felipe S.

J'ai une solution pour vous, vous pouvez ajouter une ViewController pour transférer les demandes à Angular à partir de 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", "/ à propos de", "/ tests", "/ tests/new", "/ tests/**", "/ questions", "/ answers") à mon SPAVous pouvez faire de même pour votre pré-projet et vous pouvez également rediriger votre 404 page d'erreur à la page d'index comme une étape supplémentaire . Enjoy!

41
AndroidLover

Dans mes applications Spring Boot (versions 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 des ressources statiques.

Ensuite, l’idée est de personnaliser la configuration de Spring MVC. 
La méthode la plus simple consiste à utiliser la configuration Java Spring.

J'implémente WebMvcConfigurer pour remplacer addResourceHandlers(). J'ajoute un single ResourceHandler à la version actuelle de ResourceHandlerRegistry
Le gestionnaire est mappé à chaque demande et je spécifie classpath:/static/ comme valeur d’emplacement de la ressource (vous pouvez bien sûr en ajouter d’autres si nécessaire).
J'ajoute une classe anonyme PathResourceResolver personnalisée pour remplacer 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 le retourne. Sinon, par défaut, je retourne la page index.html. Quel est le comportement attendu pour gérer les URL HTML 5.

Application Spring Boot 1.X:

L'extension de org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter est la solution.
La classe est un adaptateur de l'interface WebMvcConfigurer avec des méthodes vides permettant aux sous-classes de remplacer uniquement 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 était obsolète.
L'implémentation directe WebMvcConfigurer est la méthode actuelle, car il s'agit toujours d'une interface, mais elle dispose désormais de méthodes par défaut (rendues possibles par une ligne de base Java 8) et peut être implémentée directement sans 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");
            }
        });
    }
}
21
davidxxx

Vous pouvez transférer toutes les ressources non trouvées sur votre page principale en fournissant un fichier 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;
        }
    };
}
10
Dmitry Serdiuk

Vous pouvez transférer tout ce qui n'est pas mappé vers 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 d'API à /api pour ne pas avoir de 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());
    }
}
5
Loren

Pour simplifier les choses, vous pouvez simplement implémenter ErrorPageRegistrar directement.

@Component
public class ErrorPageConfig implements ErrorPageRegistrar {

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

}

Cela transmettrait les demandes à index.html. 

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

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

transférer tous les routages angulaires avec index.html. Y compris la base href.

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

@Controller
public class ViewController {

@RequestMapping({ "jsa/customer","jsa/customer/{id}",})
   public String index() {
       return "forward:/index.html";
   }
}

Dans mon cas jsa est base href.

0
Anis Mulla

Je l'ai fait avec un vieux vieux filter :

public class PathLocationStrategyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {

        if(request instanceof HttpServletRequest) {
            HttpServletRequest servletRequest = (HttpServletRequest) request;

            String uri = servletRequest.getRequestURI();
            String contextPath = servletRequest.getContextPath();
            if(!uri.startsWith(contextPath + "/api") && 
                !uri.startsWith(contextPath + "/assets") &&
                !uri.equals(contextPath) &&
                // only forward if there's no file extension (exclude *.js, *.css etc)
                uri.matches("^([^.]+)$")) {

                RequestDispatcher dispatcher = request.getRequestDispatcher("/");
                dispatcher.forward(request, response);
                return;
            }
        }        

        chain.doFilter(request, response);
    }
}

Puis dans web.xml:

<web-app>
    <filter>
        <filter-name>PathLocationStrategyFilter</filter-name>
        <filter-class>mypackage.PathLocationStrategyFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>PathLocationStrategyFilter</filter-name>
        <url-pattern>*</url-pattern>
    </filter-mapping>
</web-app>
0
fidke

Voici les trois étapes à suivre:

  1. Implémentez votre propre bean TomcatEmbeddedServletContainerFactory et configurez la RewriteValve

      import org.springframework.boot.context.embedded.Tomcat.TomcatEmbeddedServletContainerFactory;  
      ...
      import org.Apache.catalina.valves.rewrite.RewriteValve; 
      ... 
    
      @Bean TomcatEmbeddedServletContainerFactory servletContainerFactory() {
        TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
        factory.setPort(8080);
        factory.addContextValves(new RewriteValve());
        return factory;
      }
    
  2. Ajoutez un fichier rewrite.conf au répertoire WEB-INF de votre application et spécifiez les règles de réécriture. Voici un exemple de contenu rewrite.conf, que j’utilise dans l’application angular pour tirer parti de PathLocationStrategy de l’angulaire (en gros, je redirige tout le contenu vers index.html car nous utilisons simplement Spring Boot pour servir le contenu Web statique, sinon vous devez filtrer vos contrôleurs dans la règle RewriteCond):

      RewriteCond %{REQUEST_URI} !^.*\.(bmp|css|gif|htc|html?|ico|jpe?g|js|pdf|png|swf|txt|xml|svg|eot|woff|woff2|ttf|map)$
      RewriteRule ^(.*)$ /index.html [L]
    
  3. Supprimez useHash (ou définissez-le sur false) de vos déclarations de routage:

      RouterModule.forRoot(routes)
    

ou 

      RouterModule.forRoot(routes, {useHash: false})
0
Krzysztof Miernik