【问题标题】:Configure react to be served on Spring boot app配置响应以在 Spring Boot 应用程序上提供服务
【发布时间】:2018-05-03 01:02:34
【问题描述】:

到目前为止,我一直在为我的 Spring boot 应用程序编写一堆端点,没有 html UI 端。现在,我想提供包含反应代码的 HTML 文件和 js 文件。

当我访问 http://localhost:8443 时,我得到:

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.

Sun Nov 19 11:54:01 PST 2017
There was an unexpected error (type=Not Found, status=404).
Not Found

到目前为止我做了什么:

1.添加了一个扩展WebMvcConfigurerAdapter的Web配置类:

@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
    @Bean
    public ViewResolver internalResourceViewResolver() {
        InternalResourceViewResolver bean = new InternalResourceViewResolver();
        bean.setPrefix("/resources/static/");
        bean.setSuffix(".html");
        return bean;
    }
}

2.增加了一个休息控制器端点:

@RestController
public class HomeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView getdata() {
        ModelAndView model = new ModelAndView("index");
        return model;
    }
}

我的项目中有myproject/src/main/resources/static/index.html 文件。

【问题讨论】:

  • 您想在每个页面上都为 react 应用程序提供服务,还是需要保留一些 api-rest 端点?我的意思是你将如何区分 react 和 spring 端点,比如 /static/* 用于 react 静态文件,/api/* 用于 spring 端点,其余的 /* 用于 react index.html ?
  • 尝试从 Spring Boot 文档spring.io/guides/tutorials/react-and-spring-data-rest遵循本教程
  • 如果我理解正确的话,您只想提供静态 html 和 js 文件。在这种情况下,您不需要 Controller 或 WebMvcConfigurerAdapter,只需将 html 和 js 文件放到 src/main/resources/static (您的 index.html 所在的位置),Spring Boot 就会自动为它提供服务。
  • @OleksandrShpota 教程建议安装 Thymleaf 包,而我不使用它的任何功能。
  • @varren 这正是我想做的。我想把所有其余的api放在/api/*下,把所有与网络相关的东西放在/static/*下,并将/*路由到index.html。知道我该怎么做吗?

标签: java spring


【解决方案1】:

您知道,在 Spring Boot 2.1.4 中,新配置是:

package com.simplicity.resourceserver.configs;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
public class WebMvcConfiguration implements WebMvcConfigurer {

    private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
            "classpath:/META-INF/resources/", "classpath:/resources/",
            "classpath:/static/", "classpath:/public/" };



    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS);

        registry.addResourceHandler("/vanilla/**")
                .addResourceLocations("classpath:/vanilla/");

            registry.addResourceHandler("/react/**")
                    .addResourceLocations("classpath:/react/")
                .resourceChain(true);
    }

    @Bean
    public ViewResolver getViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setSuffix(".html");
        return resolver;
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseTrailingSlashMatch(true);
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/react")
                .setViewName("forward:/react/index.html");
        registry.addViewController("/react/{path:[^\\.]*}")
                .setViewName("forward:/react/index.html");
        registry.addViewController("/react/**/{path:[^\\.]*}")
                .setViewName("forward:/react/index.html");

    }
}

Spring Boot 应该被定义为基于“视频扑克”的编程:尝试数千次(例如当您玩视频扑克时),但细节很少不同,直到它起作用。

就我而言,我在 src/main/resources 下的两个单独文件夹中有两个不同的应用程序

  • src/main/resources/vanilla
  • src/main/resources/react

在我的例子中,它们是两个证明概念,但它可能是一个微前端架构。

【讨论】:

    【解决方案2】:

    这真的取决于您的设置。假设您想要类似的东西:

    让我们看一个简单的案例:没有 thymeleaf 模板或 spring 静态文件。 Spring 用于提供 rest api,其余的则由响应来决定。但是您可以在任何请求映射 url 上使用控制器。

    一种选择是使用ResourceResolver 并像这样配置它:

    @Configuration
    public class Config implements WebMvcConfigurerAdapter {
    
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            ResourceResolver resolver = new ReactResourceResolver();
            registry.addResourceHandler("/**")
                    .resourceChain(true)
                    .addResolver(resolver);
    
            // Can try to play with
            // registry.addResourceHandler("/**")
            //        .addResourceLocations("classpath:/static/");
            // But this option can't map every path to index.html
            // Can try https://stackoverflow.com/a/42998817/1032167
            // to resolve this, but then you loose /api/** => rest
            // and to be honest it is some regex madness, so
            // it was easier for me to setup custom resource resolver
    
    
        }
    
        public class ReactResourceResolver implements ResourceResolver {
            // root dir of react files
            // example REACT_DIR/index.html
            private static final String REACT_DIR = "/static/";
    
            // this is directory inside REACT_DIR for react static files
            // example REACT_DIR/REACT_STATIC_DIR/js/
            // example REACT_DIR/REACT_STATIC_DIR/css/
            private static final String REACT_STATIC_DIR = "static";
    
            private Resource index = new ClassPathResource(REACT_DIR + "index.html");
            private List<String> rootStaticFiles = Arrays.asList("favicon.io",
                    "asset-manifest.json", "manifest.json", "service-worker.js");
    
            @Override
            public Resource resolveResource(
                HttpServletRequest request, String requestPath,
                List<? extends Resource> locations, ResourceResolverChain chain) {
    
                return resolve(requestPath, locations);
            }
    
            @Override
            public String resolveUrlPath(
                String resourcePath, List<? extends Resource> locations,
                ResourceResolverChain chain) {
    
                Resource resolvedResource = resolve(resourcePath, locations);
                if (resolvedResource == null) {
                    return null;
                }
                try {
                    return resolvedResource.getURL().toString();
                } catch (IOException e) {
                    return resolvedResource.getFilename();
                }
            }
    
            private Resource resolve(
                String requestPath, List<? extends Resource> locations) {
    
                if (requestPath == null) return null;
    
                if (rootStaticFiles.contains(requestPath)
                        || requestPath.startsWith(REACT_STATIC_DIR)) {
                    return new ClassPathResource(REACT_DIR + requestPath);
                } else
                    return index;
            }
    
        }
    }
    

    这是 Spring 2.0.0.M4 的完整工作演示: https://github.com/varren/SpringBootReactExample


    我遇到了类似的问题,但设置略有不同:Spring single page for every url route and subroute "/a/** => /a/index.html except /a/static/**"

    还有一个选项可以使用正则表达式 Spring catch all route for index.html 来部分解决问题,但我对这种方法没有运气

    【讨论】:

    • github链接不严重,不能用Java实现类...
    【解决方案3】:

    Spring Boot 可以自动处理静态文件(按约定),只需将所有 html、js、css 等文件放到src/main/resources/static,删除您的 ViewResolver 和 Controller for '/' 就可以了,@987654322 @ 也将被 Spring Boot 映射到 /

    除此之外,您当然可以通过在您的@RestControllers 上使用正确的@RequestMapping 来制作带有api 前缀的REST 端点

    【讨论】:

      【解决方案4】:

      你需要一个返回 ModelAndView 的@Controller

          @Controller
          public class HomeController {
      
              @RequestMapping(value = {"/", "/index.html"})
              public ModelAndView sellerHome(HttpServletRequest request, 
                                             HttpServletResponse response) {
                   return new ModelAndView("../static/index");
              }
      
          }
      

      然后您可以访问http://localhost:8443,并且如果您正确配置了端口,应该会看到您的索引页面。

      【讨论】:

      • 我收到此错误:There was an unexpected error (type=Internal Server Error, status=500). javax.servlet.ServletException: Could not get RequestDispatcher for [../static/index]: Check that the corresponding file exists within your web application archive!
      猜你喜欢
      • 2019-02-13
      • 2018-09-06
      • 1970-01-01
      • 2014-02-25
      • 2019-08-06
      • 1970-01-01
      • 2016-06-12
      • 2017-02-24
      • 2019-01-14
      相关资源
      最近更新 更多