You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpringBoot+Angular单体应用部署Heroku后接口404求助

Angular + SpringBoot 单体应用部署Heroku后API接口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:

  1. 安装wait-on依赖:npm install wait-on --save
  2. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 03:17:19