SpringBoot+Angular单体应用部署Heroku后接口404求助
问题现象
本地运行完全正常,部署到Heroku后所有GET请求接口返回404。Heroku日志显示Spring Boot后端已正常启动(可见Spring标识),无报错信息;Tomcat在8080端口初始化启动,Express服务器运行在端口36455,但请求/heroNames等接口时返回404。
项目相关信息
项目结构
.mvn/wrapper Frontend src .gitignore Procfile README.md app.json mvnw mvnw.cmd package-lock.json package.json pom.xml server.js system.properties
Procfile配置
web: java -jar target/dotaApplicationTool-0.0.1-SNAPSHOT.jar & node server.js
server.js代码
const express = require('express'); const path = require('path'); const cors = require('cors'); const app = express(); // Enable CORS app.use(cors({ origin: 'https://dota-hero-matchup-cfe3a3ef874f.herokuapp.com' })); const distPath = path.join(__dirname, 'Frontend', 'dist', 'frontend'); // Set cache control headers for static files app.use(express.static(distPath, { setHeaders: (res, filePath) => { if (filePath.endsWith('.js') || filePath.endsWith('.css')) { res.setHeader('Cache-Control', 'no-store'); } }})); // Define the middleware function const setJsonContentType = (req, res, next) => { res.setHeader('Content-Type', 'application/json'); res.setHeader('Access-Control-Allow-Origin', 'https://dota-hero-matchup-cfe3a3ef874f.herokuapp.com'); next(); // Call next to proceed to the next middleware or route handler }; // Apply the middleware to all API calls app.use('/', setJsonContentType); const port = process.env.PORT || 8080; app.listen(port, () => { console.log('Server is running on port', port); });
package.json配置
{ "name": "frontend", "version": "0.0.0", "scripts": { "ng": "ng", "start": "node server.js", "build": "ng build --configuration=production", "watch": "ng build --watch --configuration development", "test": "ng test", "heroku-postbuild": "npm install --force --prefix Frontend && npm run build --prefix Frontend" }, "private": true, "dependencies": { "@angular/animations": "^15.2.0", "@angular/cli": "~15.2.4", "@angular/common": "^15.2.0", "@angular/compiler": "^15.2.0", "@angular/compiler-cli": "^15.2.0", "@angular/core": "^15.2.9", "@angular/forms": "^15.2.0", "@angular/platform-browser": "^15.2.0", "@angular/platform-browser-dynamic": "^15.2.0", "@angular/router": "^15.2.0", "cors": "^2.8.5", "express": "^4.18.2", "http-proxy-middleware": "^2.0.6", "rxjs": "~7.8.0", "tslib": "^2.3.0", "typescript": "~4.9.4", "zone.js": "~0.12.0" }, "devDependencies": { "@angular-devkit/build-angular": "^15.2.8", "@angular/cli": "~15.2.4", "@angular/compiler-cli": "^15.2.0", "@types/jasmine": "~4.3.0", "jasmine-core": "~4.5.0", "karma": "~6.4.0", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.0.0", "prettier": "2.8.8", "typescript": "~4.9.4" }, "engines": { "node": "18.15.0", "npm": "9.7.1" } }
其他配置
.gitignore包含/dist/和node_modules- Heroku使用
heroku/java和heroku/nodejs构建包,无配置变量
排查与解决思路
1. 端口与路由转发问题
Heroku每个应用仅对外暴露一个端口(由$PORT环境变量指定),当前Procfile同时启动Spring Boot(8080)和Express(使用$PORT),但外部请求只会转发到Express端口。而你的Express未配置代理转发到Spring Boot,导致API请求找不到对应路由,返回404。
解决方法:
修改server.js,添加代理配置,将API请求转发到Spring Boot的8080端口。示例:
const { createProxyMiddleware } = require('http-proxy-middleware'); // 在静态资源配置后添加代理规则,假设API前缀为/api app.use('/api', createProxyMiddleware({ target: 'http://localhost:8080', changeOrigin: true }));
同时修改Angular生产环境配置(environment.prod.ts),将API base URL设置为/api,确保前端请求走代理转发。
2. 进程启动顺序问题
使用&并行启动两个进程,可能Spring Boot尚未完全就绪,Express就开始接收请求。可通过工具等待Spring Boot启动完成后再启动Express:
- 安装
wait-on依赖:npm install wait-on --save - 修改Procfile:
web: bash -c "java -jar target/dotaApplicationTool-0.0.1-SNAPSHOT.jar & wait-on http://localhost:8080 && node server.js"
3. CORS配置冗余问题
server.js同时使用cors中间件和手动设置Access-Control-Allow-Origin头,可能导致冲突。建议保留cors中间件,移除手动设置头的代码块:
// 删除以下代码 const setJsonContentType = (req, res, next) => { res.setHeader('Content-Type', 'application/json'); res.setHeader('Access-Control-Allow-Origin', 'https://dota-hero-matchup-cfe3a3ef874f.herokuapp.com'); next(); }; app.use('/', setJsonContentType);
若需统一设置Content-Type,可在Spring Boot后端或代理配置中处理。
4. 验证Spring Boot可用性
通过Heroku命令行进入容器,直接测试Spring Boot接口:
heroku run bash curl http://localhost:8080/heroNames
若能正常返回数据,说明问题出在Express路由转发;若仍返回404,需检查Spring Boot的接口路径配置。
内容的提问来源于stack exchange,提问作者Thomarkey

