我正在开发一个基于反应的单页面应用程序的弹簧后端,我使用react-router进行客户端路由。
在index.html页面旁边,后端提供路径/api/**
上的数据。
为了在我的应用程序的根路径src/main/resources/public/index.html
上从/
提供我的index.html,我添加了一个资源处理程序
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/").addResourceLocations("/index.html");
}
我想要的是在没有其他路线匹配时提供index.html页面,例如当我调用/api
以外的路径时。
如何在春季配置这种全能路线?
答案 0 :(得分:24)
由于我的反应应用程序可以使用root作为前向目标,因此最终为我工作
@Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/{spring:\\w+}")
.setViewName("forward:/");
registry.addViewController("/**/{spring:\\w+}")
.setViewName("forward:/");
registry.addViewController("/{spring:\\w+}/**{spring:?!(\\.js|\\.css)$}")
.setViewName("forward:/");
}
}
说实话,我不知道为什么必须完全采用这种特定格式才能避免无限转发循环。
答案 1 :(得分:7)
避免@EnableWebMvc
默认情况下,Spring-Boot在src/main/resources
:
或保留@EnableWebMvc并覆盖addViewControllers
您是否指定了@EnableWebMvc
?看看这个:Java Spring Boot: How to map my app root (“/”) to index.html?
要么删除@EnableWebMvc,要么重新定义addViewControllers
:
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("forward:/index.html");
}
或定义控制器以捕获/
你可以看看这个spring-boot-reactjs sample project on github:
使用Controller执行您想要的操作:
@Controller
public class HomeController {
@RequestMapping(value = "/")
public String index() {
return "index";
}
}
其index.html
位于src/main/resources/templates
答案 2 :(得分:7)
我在Spring Boot应用程序中托管了基于Polymer的PWA,以及图像之类的静态Web资源,以及" / api /..."下的REST API。我希望客户端应用程序处理PWA的URL路由。这是我使用的:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
/**
* Ensure client-side paths redirect to index.html because client handles routing. NOTE: Do NOT use @EnableWebMvc or it will break this.
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// Map "/"
registry.addViewController("/")
.setViewName("forward:/index.html");
// Map "/word", "/word/word", and "/word/word/word" - except for anything starting with "/api/..." or ending with
// a file extension like ".js" - to index.html. By doing this, the client receives and routes the url. It also
// allows client-side URLs to be bookmarked.
// Single directory level - no need to exclude "api"
registry.addViewController("/{x:[\\w\\-]+}")
.setViewName("forward:/index.html");
// Multi-level directory path, need to exclude "api" on the first part of the path
registry.addViewController("/{x:^(?!api$).*$}/**/{y:[\\w\\-]+}")
.setViewName("forward:/index.html");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/webapp/");
}
}
这也适用于Angular和React应用程序。
答案 3 :(得分:2)
我在spring boot app中使用react和react-router,就像创建一个映射到$ gulp stylelint
的控制器和我的网站的子树一样简单/
这是我的解决方案
/users/**
此控制器未捕获Api调用,并自动处理资源。
答案 4 :(得分:1)
通过查看this question
找到答案@Bean
public EmbeddedServletContainerCustomizer notFoundCustomizer() {
return new EmbeddedServletContainerCustomizer() {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/"));
}
};
}
答案 5 :(得分:0)
要回答您在所有情况下提供单页应用(SPA)的具体问题 / api 路线,这是我修改Petri所做的工作&# 39;答案。
我有一个名为polymer的模板,其中包含我的SPA的index.html。所以挑战变成了让除了/ api和/ public-api之外的所有路线都转发到该视图。
在我的WebMvcConfigurerAdapter中,我覆盖addViewControllers并使用正则表达式:^((?!/ api / | / public-api /)。)* $
在你的情况下,你想要正则表达式:^((?!/ api /).)*$
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/{spring:^((?!/api/).)*$}").setViewName("polymer");
super.addViewControllers(registry);
}
这样就可以点击http://localhost或http://localhost/community来提供我的SPA以及SPA成功路由到http://localhost/api/posts,{{3}的所有其余呼叫等等。
答案 6 :(得分:0)
另一种解决方案(用您的路线更改/添加/删除myurl1
,myurl2
,...)
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
public class SinglePageAppController {
/**
* If the user refreshes the page while on a React route, the request will come here.
* We need to tell it that there isn't any special page, just keep using React, by
* forwarding it back to the root.
*/
@RequestMapping({"/myurl1/**", "/myurl2/**"})
public String forward(HttpServletRequest httpServletRequest) {
return "forward:/";
}
}
注意:使用public String index()
也可以正常工作,但前提是您使用模板。并且不推荐使用WebMvcConfigurerAdapter
。
答案 7 :(得分:0)
经过多次尝试,我发现以下解决方案是最简单的解决方案。它基本上会绕过所有难以处理的 Spring 处理。
@Component
public class StaticContentFilter implements Filter {
private List<String> fileExtensions = Arrays.asList("html", "js", "json", "csv", "css", "png", "svg", "eot", "ttf", "woff", "appcache", "jpg", "jpeg", "gif", "ico");
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
}
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String path = request.getServletPath();
boolean isApi = path.startsWith("/api");
boolean isResourceFile = !isApi && fileExtensions.stream().anyMatch(path::contains);
if (isApi) {
chain.doFilter(request, response);
} else if (isResourceFile) {
resourceToResponse("static" + path, response);
} else {
resourceToResponse("static/index.html", response);
}
}
private void resourceToResponse(String resourcePath, HttpServletResponse response) throws IOException {
InputStream inputStream = Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream(resourcePath);
if (inputStream == null) {
response.sendError(NOT_FOUND.value(), NOT_FOUND.getReasonPhrase());
return;
}
inputStream.transferTo(response.getOutputStream());
}
}