首页 > 解决方案 > Spring Boot React 在单独的包中

问题描述

我有一个 Spring Boot,Java 8 API,我想添加一个 React 前端,以使 API 操作更易于导航和使用。我使用JHipster生成器生成了一个 React 应用程序。然后我只是把 web 部分放到我的应用程序中,但是每当我运行 Spring Boot 应用程序并尝试导航到移植:8080它时,我都会得到以下页面:

在此处输入图像描述

并且控制台中出现以下错误:

2019-07-15 15:07:21.414  INFO 29145 --- [)-10.15.114.229] o.a.c.c.C.[.[.[/]                                            : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-07-15 15:07:21.414  INFO 29145 --- [)-10.15.114.229] o.s.w.s.DispatcherServlet                                    : Initializing Servlet 'dispatcherServlet'
2019-07-15 15:07:21.414 DEBUG 29145 --- [)-10.15.114.229] o.s.w.s.DispatcherServlet                                    : Detected StandardServletMultipartResolver
2019-07-15 15:07:21.426 DEBUG 29145 --- [)-10.15.114.229] o.s.w.s.DispatcherServlet                                    : enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
2019-07-15 15:07:21.426  INFO 29145 --- [)-10.15.114.229] o.s.w.s.DispatcherServlet                                    : Completed initialization in 12 ms
2019-07-15 15:07:25.079 DEBUG 29145 --- [nio-8080-exec-1] o.s.w.s.DispatcherServlet                                    : GET "/", parameters={}
2019-07-15 15:07:25.093 DEBUG 29145 --- [nio-8080-exec-1] s.d.s.w.PropertySourcedRequestMappingHandlerMapping          : looking up handler for path: /
2019-07-15 15:07:25.128 DEBUG 29145 --- [nio-8080-exec-1] o.s.w.s.h.SimpleUrlHandlerMapping                            : Mapped to ResourceHttpRequestHandler ["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/", "/"]
2019-07-15 15:07:25.130 DEBUG 29145 --- [nio-8080-exec-1] o.s.w.s.r.ResourceHttpRequestHandler                         : Resource not found
2019-07-15 15:07:25.131 DEBUG 29145 --- [nio-8080-exec-1] o.s.w.s.DispatcherServlet                                    : Completed 404 NOT_FOUND
2019-07-15 15:07:25.152 DEBUG 29145 --- [nio-8080-exec-1] o.s.w.s.DispatcherServlet                                    : "ERROR" dispatch for GET "/error", parameters={}
2019-07-15 15:07:25.153 DEBUG 29145 --- [nio-8080-exec-1] s.d.s.w.PropertySourcedRequestMappingHandlerMapping          : looking up handler for path: /error
2019-07-15 15:07:25.167 DEBUG 29145 --- [nio-8080-exec-1] o.s.w.s.m.m.a.RequestMappingHandlerMapping                   : Mapped to public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2019-07-15 15:07:25.199 DEBUG 29145 --- [nio-8080-exec-1] o.s.w.s.v.ContentNegotiatingViewResolver                     : Selected 'text/html' given [text/html, text/html;q=0.8]
2019-07-15 15:07:25.208 DEBUG 29145 --- [nio-8080-exec-1] o.s.w.s.DispatcherServlet                                    : Exiting from "ERROR" dispatch, status 404

这是我的项目结构:

FooApplication
├── build.gradle
├── api/
    ├── src.main.java.com.foo/
            ├── FooApplication.java
    └── build.gradle
├── web/
    ├── node_modules/
    ├── src.main.webapp/
        ├── app/
            ├── index.tsx
            ├── routes.tsx
            └── app.tsx
        └── index.html
    ├── webpack/
        └── webpack.common.js
    └── build.gradle

这是我的webpack.common.js文件:

const webpack = require('webpack');
const {BaseHrefWebpackPlugin} = require('base-href-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const path = require('path');

const utils = require('./utils.js');

const getTsLoaderRule = env => {
  const rules = [
    {
      loader: 'cache-loader',
      options: {
        cacheDirectory: path.resolve('build/cache-loader')
      }
    },
    {
      loader: 'thread-loader',
      options: {
        // There should be 1 cpu for the fork-ts-checker-webpack-plugin.
        // The value may need to be adjusted (e.g. to 1) in some CI environments,
        // as cpus() may report more cores than what are available to the build.
        workers: require('os').cpus().length - 1
      }
    },
    {
      loader: 'ts-loader',
      options: {
        transpileOnly: true,
        happyPackMode: true
      }
    }
  ];
  if (env === 'development') {
    rules.unshift({
      loader: 'react-hot-loader/webpack'
    });
  }
  return rules;
};

module.exports = options => ({
  cache: options.env !== 'production',
  resolve: {
    extensions: [
      '.js', '.jsx', '.ts', '.tsx', '.json'
    ],
    modules: ['node_modules'],
    alias: {
      app: utils.root('src/main/webapp/app/')
    }
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: getTsLoaderRule(options.env),
        include: [utils.root('./src/main/webapp/app')],
        exclude: [utils.root('node_modules')]
      },
      {
        test: /\.(jpe?g|png|gif|svg|woff2?|ttf|eot)$/i,
        loader: 'file-loader',
        options: {
          digest: 'hex',
          hash: 'sha512',
          name: 'content/[hash].[ext]'
        }
      },
      {
        enforce: 'pre',
        test: /\.jsx?$/,
        loader: 'source-map-loader'
      },
      {
        test: /\.tsx?$/,
        enforce: 'pre',
        loader: 'tslint-loader',
        exclude: [utils.root('node_modules')]
      }
    ]
  },
  stats: {
    children: false
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: `'${options.env}'`,
        VERSION: `'${utils.parseVersion()}'`,
        DEBUG_INFO_ENABLED: options.env === 'development',
        // The root URL for API calls, ending with a '/' - for example: `"https://www.jhipster.tech:8081/myservice/"`.
        // If this URL is left empty (""), then it will be relative to the current context.
        // If you use an API server, in `prod` mode, you will need to enable CORS
        // (see the `jhipster.cors` common JHipster property in the `application-*.yml` configurations)
        SERVER_API_URL: `''`
      }
    }),
    new ForkTsCheckerWebpackPlugin({tslint: true}),
    new CopyWebpackPlugin([
      {from: './node_modules/swagger-ui/dist/css', to: 'swagger-ui/dist/css'},
      {from: './node_modules/swagger-ui/dist/lib', to: 'swagger-ui/dist/lib'},
      {from: './node_modules/swagger-ui/dist/swagger-ui.min.js', to: 'swagger-ui/dist/swagger-ui.min.js'},
      {from: './src/main/webapp//swagger-ui/', to: 'swagger-ui'},
      {from: './src/main/webapp/static/', to: 'content'},
      {from: './src/main/webapp/favicon.ico', to: 'favicon.ico'},
      {from: './src/main/webapp/manifest.webapp', to: 'manifest.webapp'},
      // jhipster-needle-add-assets-to-webpack - JHipster will add/remove third-party resources in this array
      {from: './src/main/webapp/robots.txt', to: 'robots.txt'}
    ]),
    new HtmlWebpackPlugin({
      template: './src/main/webapp/index.html',
      chunksSortMode: 'dependency',
      inject: 'body'
    }),
    new BaseHrefWebpackPlugin({baseHref: '/'}),
  ]
});

index.html导航到时如何让 spring boot 提供文件localhost:8080?我知道我将不得不覆盖一些默认的 Spring Boot 配置才能使其正常工作,但我不清楚哪些配置。

我可以根据要求提供更多文件的详细信息,只是不想用不必要的信息使帖子超载。

标签: javaspringreactjsspring-bootjhipster

解决方案


Spring-boot 默认将一些路径自动配置为静态资源位置/static/public/resources. 查看官方 Spring 文档

因此,如果您可以将 index.html 和相关资产移动到静态资源路径中,Spring Boot 应该按原样提供它们。
或者,正如那些相同的文档所说,您可以通过设置spring.resources.static-locations属性将新路径添加为静态位置。

一个不错的做法是构建您的 React 应用程序,捆绑资源,并将捆绑的资产 + index.html 复制到 Spring 项目的静态位置。

你没有提到任何关于你的构建设置,如果你使用带有 Webpack 的 Gradle Node 插件,那么这应该不难。这篇博文展示了你将如何做到这一点(但是,不使用多个模块)


推荐阅读