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?
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ß!
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");
}
});
}
}
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;
}
};
}
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());
}
}
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:/";
}
}
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.
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>
Dies sind die drei Schritte, die Sie befolgen müssen:
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;
}
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]
Befreien Sie sich von useHash (oder setzen Sie es auf "false") von Ihren Routing-Deklarationen:
RouterModule.forRoot(routes)
oder
RouterModule.forRoot(routes, {useHash: false})