web-dev-qa-db-de.com

Springboot/Angular2 - Umgang mit HTML5-URLs

Ich glaube, das ist eine einfache Frage, aber ich konnte keine Antwort finden oder zumindest die richtigen Begriffe in der Suche verwenden.

Ich setze Angular2 und Springboot zusammen. Standardmäßig verwendet Angular Pfade wie localhost:8080\dashboard und localhost:8080\dashboard\detail.

Ich möchte, wenn möglich, die Verwendung von path als hashs vermeiden. Als Angular Dokumentation heißt es:

Mit der Funktion mountRouter des Routers wird LocationStrategy auf PathLocationStrategy festgelegt, wodurch es zur Standardstrategie wird. Wir können während des Bootstrapping-Vorgangs mit einer Überschreibung zum HashLocationStrategy wechseln, wenn wir dies vorziehen.

Und dann...

Fast alle Angular 2-Projekte sollten den Standard-HTML 5-Stil verwenden. Es erzeugt URLs, die für Benutzer leichter verständlich sind. Außerdem bleibt die Option für das serverseitige Rendern später erhalten.

Das Problem ist, dass Spring beim Versuch, auf localhost:8080\dashboard zuzugreifen, nach einer Controller-Zuordnung zu diesem Pfad sucht, die er nicht hat.

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

Ich dachte zunächst, alle meine Dienste unter localhost:8080\api und alle meine Statik unter localhost:8080\app zu machen. Aber wie kann ich Spring anweisen, Anforderungen an diesen app-Pfad zu ignorieren?

Gibt es eine bessere Lösung mit Angular2 oder Boot?

32
Felipe S.

Ich habe eine Lösung für Sie, Sie können eine ViewController hinzufügen, um Anforderungen von Spring Boot an Angular weiterzuleiten.

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";
   }
}

hier habe ich alle meine angle2 umgeleitet ("/ bikes", "/ milages", "/ gallery", "/ tracks", "/ tracks/{id:\w +}", "/ location", "/ about", "/ tests", "/ tests/new", "/ tests/**", "/ fragen", "/ answers") in meinem SPA Sie können dasselbe für Ihr Vorobjekt tun und Sie können auch Ihr 404 Fehlerseite zur Indexseite als weiteren Schritt . Viel Spaß!

41
AndroidLover

In meinen Spring Boot-Anwendungen (Version 1 und 2) befinden sich meine statischen Ressourcen an einem einzigen Ort:

src/main/resources/static

static ist ein Ordner, der von Spring Boot zum Laden statischer Ressourcen erkannt wird.

Dann sollten Sie die Spring MVC-Konfiguration anpassen. 
Der einfachere Weg ist die Spring Java-Konfiguration.

Ich implementiere WebMvcConfigurer, um addResourceHandlers()..__ zu überschreiben. Ich füge ein single ResourceHandler zur aktuellen ResourceHandlerRegistry hinzu. 
Der Handler wird bei jeder Anforderung zugeordnet, und ich gebe classpath:/static/ als Ressourcenstandortwert an (Sie können natürlich weitere hinzufügen, wenn dies erforderlich ist).
Ich füge eine benutzerdefinierte PathResourceResolver anonyme Klasse hinzu, um getResource(String resourcePath, Resource location) zu überschreiben.
Die Regel für die Rückgabe der Ressource lautet wie folgt: Wenn die Ressource vorhanden und lesbar ist (also eine Datei ist), gebe ich sie zurück. Ansonsten gebe ich standardmäßig die Seite index.html zurück. Welches Verhalten wird erwartet, um HTML 5-URLs zu behandeln.

Spring Boot 1.X Anwendung:

org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter zu erweitern ist der Weg.
Die Klasse ist ein Adapter der WebMvcConfigurer-Schnittstelle Mit leeren Methoden können Unterklassen nur die Methoden überschreiben, an denen sie interessiert sind. 

Hier ist der vollständige Code:

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");
            }
        });
    }
}

Spring Boot 2.X Anwendung:

org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter war veraltet.
Die direkte Implementierung von WebMvcConfigurer ist jetzt der Weg, da es sich immer noch um eine Schnittstelle handelt, aber es verfügt jetzt über Standardmethoden (die durch eine Java 8-Baseline möglich werden), und kann direkt ohne Adapter implementiert werden. 

Hier ist der vollständige Code:

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

Sie können alle nicht gefundenen Ressourcen an Ihre Hauptseite weiterleiten, indem Sie einen benutzerdefinierten ErrorViewResolver bereitstellen. Alles, was Sie tun müssen, ist, dies Ihrer @Configuration-Klasse hinzuzufügen:

@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

Sie können alles, was nicht in Angular abgebildet ist, wie folgt weiterleiten:

@Controller
public class ForwardController {

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

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

Mein Spring Boot-Server für angle ist auch ein Gateway-Server mit den API-Aufrufen für /api, um keine Anmeldeseite vor den eckigen Seiten zu haben.

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

Um es einfacher zu machen, können Sie ErrorPageRegistrar einfach direkt implementieren.

@Component
public class ErrorPageConfig implements ErrorPageRegistrar {

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

}

Dies würde die Anforderungen an index.html weiterleiten. 

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

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

alles Angular Routing mit index.html weiterleiten. Einschließlich Basishref.

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";
   }
}

In meinem Fall ist jsa base href.

0
Anis Mulla

Ich habe es mit einem einfachen alten filter gemacht:

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);
    }
}

Dann in 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

Dies sind die drei Schritte, die Sie befolgen müssen:

  1. Implementieren Sie Ihre eigene TomcatEmbeddedServletContainerFactory-Bean und richten Sie das RewriteValve ein

      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. Fügen Sie dem WEB-INF-Verzeichnis Ihrer Anwendung eine rewrite.conf -Datei hinzu, und geben Sie die Umschreiberegeln an. Hier ist ein Beispiel für den Inhalt der Datei rewrite.conf, den ich in der Winkelanwendung verwende, um die PathLocationStrategy des Winkels zu nutzen (im Grunde leite ich einfach alles zur index.html um, da wir Spring Boot verwenden, um statischen Webinhalt bereitzustellen, andernfalls Sie müssen Ihre Controller in der RewriteCond-Regel herausfiltern.)

      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. Befreien Sie sich von useHash (oder setzen Sie es auf "false") von Ihren Routing-Deklarationen:

      RouterModule.forRoot(routes)
    

oder 

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