我有一个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配置才能使其正常工作,但是我不清楚什么配置。
我可以根据要求提供更多文件的详细信息,只是不想让不必要的信息超载。
答案 0 :(得分:0)
为应用程序的入口点请求映射,读取文件,然后返回内容。就我而言,React代码存储在/tmp/build/index.html。简化并忽略了错误,看起来可能像这样:
@RequestMapping(
value = "",
method = RequestMethod.GET)
public ResponseEntity<String> getIndexContent() {
final File file = new File("/tmp/build/index.html");
final String content =
FileUtils.readFileToString(
file,
StandardCharsets.UTF_8);
return ResponseEntity
.ok()
.contentType(MediaType.TEXT_HTML)
.body(content);
}
答案 1 :(得分:0)
Spring-boot默认将某些路径自动配置为静态资源位置:return these 3 ids
,# example data
mydata = np.array([0.85405058, 0.78228784, np.nan, 0.72828138, 0.73757833, 0.69303712, 0.5730553, 0.57644895])
dates_list = [datetime.date(2017, 1, 5), datetime.date(2017, 5, 22),
datetime.date(2017, 6, 14), datetime.date(2017, 8, 17), datetime.date(2017, 9, 27), datetime.date(2017, 10, 6), datetime.date(2017, 11, 23), datetime.date(2017, 12, 28)]
months_list= [1, 5, 6, 8, 9, 10, 11, 12]
# plot data
fig, ax = plt.subplots(1,1)
ax.scatter(dates_list,mydata,s=30,c=months_list,cmap='viridis')
或/static
。参见Official Spring Docs
因此,如果您可以将index.html和相关资产移动到静态资源路径中,则Spring Boot应该按原样提供它们。
另外,就像那些文档所说的那样,您可以通过设置/public
属性来将新路径添加为静态位置。
要做的一件好事是构建您的React应用程序,捆绑资源,并将捆绑的资产+ index.html复制到Spring项目的静态位置。
您还没有提到有关构建设置的任何信息,如果您将Gradle Node Plugin与Webpack一起使用,那么这并不难。 blog post展示了您将如何操作(但是,不要使用多个模块)