Springboot / Angular2 - jak obsługiwać adresy URL HTML5?
Uważam, że to proste pytanie, ale nie mogłem znaleźć odpowiedzi lub przynajmniej użyć poprawnych terminów w wyszukiwaniu.
Przygotowuję Angular2
i Springboot
razem. Domyślnie Angular
będzie używać ścieżek takich jak localhost:8080\dashboard
i localhost:8080\dashboard\detail
.
Chciałbym unikać używania path jako hashów, jeśli to możliwe. Jak wynika z Dokumentacji :
Funkcja provideRouter ustawia LocationStrategy na PathLocationStrategy, czyniąc ją domyślną strategia. Możemy przełączyć się na HashLocationStrategy z nadpisaniem podczas procesu bootstrapowania, jeśli wolimy.
I wtedy...
Prawie wszystkie projekty Angular 2 powinny używać domyślnego stylu HTML 5. Tworzy adresy URL, które są łatwiejsze do zrozumienia dla użytkowników. I zachowuje opcję późniejszego renderowania po stronie serwera.
Problem polega na tym, że gdy spróbuję uzyskać dostęp localhost:8080\dashboard
, Spring będzie szukał jakiegoś kontrolera mapującego tę ścieżkę, który to Nie będę.
Whitelabel Error Page
There was an unexpected error (type=Not Found, status=404).
No message available
Początkowo myślałem, aby wszystkie moje usługi były pod localhost:8080\api
i wszystkie moje statyczne Pod localhost:8080\app
. Ale jak mam powiedzieć Spring ' owi, żeby zignorował prośby do tej ścieżki app
?
Czy jest lepsze rozwiązanie z Angular2 lub Boot?
8 answers
Mam dla Ciebie rozwiązanie, możesz dodać ViewController
Aby przesłać żądania do Angular Z 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";
}
}
Tutaj przekierowałem wszystkie moje angular2 ("/rowery", "/kilometry", "/Galeria", "/Tory", "/Tory/{id:\w+}", "/Lokalizacja", "/o", "/testy","/testy/nowe","/testy/**","/pytania","/odpowiedzi") do mojego SPA
Możesz zrobić to samo dla wstępnego projektu, a także przekierować stronę błędu 404 na stronę indeksu jako kolejny krok.
Smacznego!Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-29 21:52:37
W moich aplikacjach Spring Boot (Wersja 1 i 2) Moje zasoby statyczne są w jednym miejscu :
src/main/resources/static
static
jako folder rozpoznawany przez Spring Boot do ładowania zasobów statycznych.
Następnie chodzi o dostosowanie konfiguracji Spring MVC.
Prostszym sposobem jest użycie konfiguracji Spring Java.
Implementuję WebMvcConfigurer
, aby zastąpić addResourceHandlers()
.
Dodaję singiel ResourceHandler
do prądu ResourceHandlerRegistry
.
Handler jest mapowany na każde żądanie i określam classpath:/static/
jako wartość lokalizacji zasobów (możesz oczywiście dodać inne, jeśli jest to wymagane).
Dodaję niestandardową klasę PathResourceResolver
anonymous, aby nadpisać getResource(String resourcePath, Resource location)
.
A reguła zwracania zasobu jest następująca: jeśli zasób istnieje i jest czytelny( więc jest to plik), zwracam go. W przeciwnym razie domyślnie zwracam stronę index.html
. Co jest oczekiwanym zachowaniem obsługi adresów URL HTML 5.
Sprężynowy But 1.X Zastosowanie:
Rozszerzenie org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
jest drogą.
klasa jest adapter interfejsu {[5] }
z pustymi metodami pozwalającymi podklasom nadpisywać tylko te metody, którymi są zainteresowani.
Oto Pełny kod:
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 Zastosowanie:
org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
był przestarzały.
Implementacja bezpośrednio WebMvcConfigurer
jest teraz sposobem, ponieważ jest nadal interfejsem, ale ma teraz domyślne metody (możliwe dzięki linii bazowej Java 8) i może być zaimplementowana bezpośrednio bez potrzeby stosowania adaptera.
Oto Pełny kod:
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");
}
});
}
}
EDIT to address some comments:
Dla tych, którzy przechowują swoje zasoby statyczne w innej lokalizacji jako src/main/resources/static
, należy zmienić wartość parametru var args z addResourcesLocations()
.
Na przykład, jeśli masz zasoby statyczne zarówno w static
, jak i w folderze public
(Nie próbowano):
registry.addResourceHandler("/**/*")
.addResourceLocations("classpath:/static/", "/public")
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-07 16:21:22
Możesz przesłać wszystkie Nie znalezione zasoby do swojej strony głównej, dostarczając Niestandardowy ErrorViewResolver. Wszystko, co musisz zrobić, to dodać to do swojej klasy @ 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;
}
};
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-27 09:48:07
Możesz przesłać wszystko, co nie jest odwzorowane na kątowe, używając czegoś takiego:
@Controller
public class ForwardController {
@RequestMapping(value = "/**/{[path:[^\\.]*}")
public String redirect() {
// Forward to home page so that route is preserved.
return "forward:/";
}
}
Źródło: https://stackoverflow.com/a/44850886/3854385
Mój Spring Boot server dla angular jest również serwerem bramy z wywołaniami API do /api
, aby nie mieć strony logowania przed stronami angular, możesz użyć czegoś takiego.
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());
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-17 23:07:02
Aby to uprościć, możesz po prostu zaimplementować ErrorPageRegistrar bezpośrednio..
@Component
public class ErrorPageConfig implements ErrorPageRegistrar {
@Override
public void registerErrorPages(ErrorPageRegistry registry) {
registry.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/"));
}
}
To przekaże żądania do indeksu.html.
@Controller
@RequestMapping("/")
public class MainPageController {
@ResponseStatus(HttpStatus.OK)
@RequestMapping({ "/" })
public String forward() {
return "forward:/";
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-15 20:48:24
Oto trzy kroki, które musisz wykonać:
-
Zaimplementuj własny tomcatembeddedservletcontainerfactory bean i skonfiguruj 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; }
-
Dodaj przepisanie.plik conf do katalogu WEB-INF Twojej aplikacji i określ reguły przepisywania. Oto przykład przepisania.conf content, który używam w aplikacji angular, aby skorzystać z PathLocationStrategy Angulara (zasadniczo po prostu Przekierowuję wszystko do indeksu.html jak my wystarczy użyć spring boot, aby obsługiwać statyczną zawartość sieci, w przeciwnym razie musisz odfiltrować Kontrolery w regule 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]
-
Pozbądź się useHash (lub ustaw go na false) z deklaracji routingu:
RouterModule.forRoot(routes)
Lub
RouterModule.forRoot(routes, {useHash: false})
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-08 10:23:32
Prześlij wszystkie trasy kątowe z indeksem.html. Łącznie z bazą 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";
}
}
W moim przypadku JSA to baza href.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-23 05:57:49
Zrobiłem to zwykłym starym filtrem :
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);
}
}
Następnie w 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>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-07 00:26:42