使用Spring Boot托管单页应用程序

7

我正在尝试使用Spring将单页面应用程序(SPA)与普通REST API一起托管。

这意味着所有请求都应由相应的控制器处理,而其他请求应重定向到文件夹/static/built中的资源。

我通过捕获所有NoHandlerFoundExceptions并将其重定向到js文件或html文件,并使用WebMvcConfigurer映射静态内容来使其工作。

但是,这似乎对我来说不太正式,那么有没有更好的方法可以实现同样的功能呢?


你只需要将构建好的前端文件复制到Spring Boot的static/目录中(你可以使用Maven前端插件来自动化)。然后,Spring Boot会将它们作为静态文件提供,你不需要担心重定向到HTML或JS。如果你在SPA中使用基于哈希的URL(如so.com/#/route/...),那么在Spring中就没有更多要做的了。 - Matt
4个回答

6
成功将React+ReactRouter应用程序工作,方法是添加以下映射:
@Controller
public class RedirectController {

  @GetMapping(value = {"/{regex:\\w+}", "/**/{regex:\\w+}"})
  public String forward404() {
    return "forward:/";
  }

}

此外,您需要在application.properties文件中添加配置spring.mvc.pathmatch.matching-strategy=ant_path_matcher,以允许/激活**模式。否则,您的应用程序将无法启动。
这个想法来自于https://dev59.com/VVkS5IYBdhLWcg3w5KAh#42998817

1
连字符不是\w正则表达式字符,但可以在端点中使用,因此请将\\w替换为[\\w-](正则表达式方括号以捕获额外的连字符字符)。句点.也是一样,但作为端点名称较少见,可能指的是静态文件资源,因此我不会包含它。 - George Pantazes

2

我让我的单页应用(SPA)与Spring后端API配合工作的最简单方法是使用两个不同的控制器:一个用于SPA的根索引页面,另一个控制器用于管理各种RESTful API端点:

这是我的两个控制器:

MainController.java

APIController.java

@Controller
public class MainController {

    @RequestMapping(value = "/")
    public String index() {
        return "index";
    }
}

MonitoringController.java

@RestController
@RequestMapping(value = "api")
public class MonitoringEndpoints {

    @GetMapping(path = "/health", produces = "application/hal+json")
    public ResponseEntity<?> checkHealth() throws Exception {
        HealthBean healthBean = new HealthBean();
        healthBean.setStatus("UP");

        return ResponseEntity.ok(healthBean);
    }
}

注意API端点控制器如何使用'@RestController'注释,而主控制器使用'@Controller'注释。这是由于Thymeleaf如何利用其ViewResolver。请参见:Spring Boot MVC, not returning my view
现在,请将您的index.html页面放置在src/main/resources/templates/index.html中,因为Spring默认查找此位置内的html页面。
我的index.html页面看起来像这样:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head lang="en">
        <meta charset="UTF-8"/>
        <title>EDP Monitoring Tool</title>
    </head>
    <body>
        <!-- Entry point to our ReactJS single page web application -->
        <div id="root"></div>

        <script type="text/javascript" src="built/bundle.js"></script>
    </body>
</html>

如果您不确定如何连接前端,无论是一个ReactJS应用程序还是其他什么,我相信这些信息会对您有所帮助。如果您有其他问题,请告诉我。

此外,如果您正在使用Webpack,您可以通过webpack.config.js文件中的entry键设置JS文件的入口点,就像这样:

Original Answer翻译成“最初的回答”

entry: ['./src/main/js/index.js']

0
我回答这个问题是因为我看到了很多不同的方法,包括使用带有正则表达式的控制器(如上所示)甚至使用404处理程序来提供文件。实际上还有另一种方法:使用旧式的Servlet过滤器来实现这一点。我已经发布了完整的代码:https://github.com/chiralsoftware/SpringSinglePageApp 关键是使用WebMvcConfigurer来添加一个ResourceHandler
public class WebConfiguration implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/*").addResourceLocations("file:/opt/web/singlepageapp/");
    }

非常重要的是注意路径中的file:前缀,以及末尾的/
然后使用一个传统的过滤器,并配合HttpServletRequestWrapper使用。
@WebFilter("/*")
public class IndexFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if(indexLocation == null || indexLocation.isBlank()) {
            LOG.warning("The indexLocation value was null or blank so this filter is disabled.");
            chain.doFilter(request, response);
            return;
        }
        final HttpServletRequest httpRequest = (HttpServletRequest) request;
        
        final String relativePath = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length());
        LOG.finest("the relative path is: " + relativePath);

        if(staticFiles.stream().anyMatch(f -> relativePath.startsWith(f))) {
            LOG.finest("it's a static file so chain forward");
            chain.doFilter(request, response);
            return;
        }
        
        chain.doFilter(new MyServletRequest(httpRequest), response);
    }
    
    private final class MyServletRequest extends HttpServletRequestWrapper {
        
        public MyServletRequest(HttpServletRequest request) {
            super(request);
        }
        
        @Override
        public String getRequestURI() {
            return getServletContext().getContextPath() + indexLocation;
        }
                
    }

请查看Github链接获取完整的代码。它完全按照需要工作,并且是使用Spring来提供Vue单页应用程序的好方法。真正的技巧是利用FilterHttpServletRequestWrapper的强大功能,这是不明显的。在Nginx中,这只需要一行代码,但是这种方法让你可以在Spring中完成所有操作。我还在存储库中包含了Jetty部署文件。

-3

这个问题与Apache .htaccess无关。 - Suyash

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接