在 VPS 上使用 Apache2 代理时,Node.js API 请求缺少 /api 前缀(本地工作)

问题描述 投票:0回答:1

问题:

我有一个 MERN 堆栈项目,其后端 API 在 VPS 服务器上运行。我的设置在本地计算机上完美运行,但当我将其部署到 VPS 时,API 请求缺少 /api 前缀,导致我的网站上出现 404 Not Found 错误。

环境:

前端:Vite(React)

后端: Node.js 与 PM2 一起运行

Web 服务器: VPS 上的 Apache2 (Ubuntu 20.04)

代理: Apache2 正在处理后端 API 的反向代理。

问题: 当我从前端发送请求时,例如 /api/v1/auth/login,后端收到请求,但没有像 //v1/auth/login 这样的 api 前缀,导致路由不正确,我收到 404 Not Found错误。然而,本地一切正常。

我的 apache 虚拟主机

<VirtualHost *:80>
ServerAdmin webmaster@localhost
ServerName agileboard.muhammadzeeshan.dev
DocumentRoot /var/www/agileboard.muhammadzeeshan.dev/react-app/dist
ProxyRequests Off
ProxyPreserveHost On
ProxyPass "/api/v1" "http://localhost:5000/api/v1"
ProxyPassReverse "/api/v1" "http://localhost:5000/api/v1"

<Directory /var/www/agileboard.muhammadzeeshan.dev/react-app/dist>
    AllowOverride All
    Require all granted
    FallbackResource /index.html
</Directory>

ErrorLog ${APACHE_LOG_DIR}/agileboard_error.log
CustomLog ${APACHE_LOG_DIR}/agileboard_access.log combined

 RewriteEngine on
 RewriteCond %{SERVER_NAME} =www.agileboard.muhammadzeeshan.dev [OR]
 RewriteCond %{SERVER_NAME} =agileboard.muhammadzeeshan.dev
 RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

Vite 配置(本地代理设置): 在我的本地计算机上,我在开发过程中使用以下代理配置将请求从前端路由到后端:

 export default defineConfig(({ command }) => {
   if (command === 'serve' || command === 'preview' || command === 'dev' ) {
    // development config
     return {
      plugins: [react()],
      server: {
       proxy: {
        '/api': {
          target: 'http://localhost:5000/api',
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, ''),
        },
      },
    },
     build: {
        rollupOptions: {
          output: {
            manualChunks(id) {
              if (id.includes('node_modules')) {
                // Split each dependency into its own chunk
                return id.split('node_modules/')[1].split('/')[0];
              }
              if (id.includes('froala-editor')) {
                return 'froala-editor';
              }
            },
          },
        },
      },
  };


 } else {

   // Production config
 return {
   plugins: [react()],
   build: {
      rollupOptions: {
        output: {
          manualChunks(id) {
            if (id.includes('node_modules')) {
              // Split each dependency into its own chunk
              return id.split('node_modules/')[1].split('/')[0];
            }
            if (id.includes('froala-editor')) {
              return 'froala-editor';
            }
           },
         },
       },
     },
   };
  }
 });

我的节点 APi 的 server.js 通过 pm2 运行

import 'express-async-errors';
import express from "express";
import cors from 'cors';
import  config  from './config/default.js';
import morgan from 'morgan';
import errorHandlerMiddleware from './middleware/errorHandlerMiddleware.js';
import cookieParser from 'cookie-parser';
import { initModels } from './models/index.js';
import path from 'path';
import { fileURLToPath, pathToFileURL } from 'url';
import os from 'os';
// Get the current directory
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);


const app = express();

// Middleware
  app.use(cors());
  app.use(cookieParser());
  app.use(express.json());


  if(config.node_env ==="development"){
   app.use(morgan("dev"))
  }
  // Define your routes here
  import Route  from './routes/routes.js';
  app.use('/api/v1/', Route);


  // middleware to check for errors by controllers, this itself will be valid request
  app.use(errorHandlerMiddleware);

   app.use((req, res, next) => {
    res.set('Cache-Control', 'no-store');
  next();
 });

 app.get('/api/v1/api_testing', (req, res) => {
 res.json({ message: 'API working' });
 });

  app.use((req, res, next) => {
 console.log(`${req.method} ${req.url}`);
  next();
 });


  //  middleware to check for invalid request errors
  app.use('*',(req, res)=>{
   return res.status(404).json({message:"404 Resource not found"})
  });

 try{

  const server = app.listen(config.port, async () => {
  await initModels();

  const { address, port } = server.address();
  const host = address === '::' ? 'localhost' : address
  console.log('Database synchronized successfully');

  console.log(`Server running at http:// ${host} : ${port} `);
  });

}catch(err){
 console.error('Error: '+ err);
 process.exit(1);
}

前端 React 应用程序中的 Axios 请求包装器

const CustomRequest = axios.create({
 baseURL: "/api/v1"
});

pm2 日志

Server running at http:// localhost : 5000
  POST //v1/auth/login
  POST //v1/auth/login
  POST //v1/auth/login
  POST //v1/auth/login
  POST //v1/auth/login
  POST //v1/auth/login
  POST //v1/auth/login
  GET //v1/api_testing
  GET //v1/api_testing
  GET //v1/api_testing
  GET //v1/api_testing
  GET //v1/api_testing
  POST //v1/auth/login
  POST //v1/auth/login
  POST //v1/auth/login
  POST //v1/auth/login

我尝试过的: 使用 LogLevel debug 检查 Apache 日志。这是一些相关的日志条目

[proxy:debug] [pid 12345] proxy_util.c(1936): AH00925: initializing worker 
 http://localhost:5000/api/v1 shared
[proxy:debug] [pid 12345] proxy_util.c(1993): AH00927: initializing worker 
 http://localhost:5000/api/v1 local

已验证 ProxyPreserveHost 已启用。 尝试修改 Apache ProxyPass 和 ProxyPassReverse 设置。

附加信息:

  1. 到达后端的请求缺少 /api 前缀(例如, //v1/auth/login 而不是 /api/v1/auth/login)。
  2. 在本地环境中一切正常,但在 VPS 上失败 相同的配置。

问题: 为什么 Apache2 在代理 Node.js API 请求时剥离 /api 前缀?如何确保保留 /api 前缀?

任何帮助或建议将不胜感激!

reactjs node.js proxy apache2 vite
1个回答
0
投票

我在一位 LinkedIn 朋友的帮助下找到了解决方案。 所以问题是在这种类型的虚拟主机配置中

<VirtualHost *:80>
  ServerAdmin webmaster@localhost
  ServerName agileboard.muhammadzeeshan.dev
  DocumentRoot /var/www/agileboard.muhammadzeeshan.dev/react-app/dist
  ProxyRequests Off
  ProxyPreserveHost On
  ProxyPass "/api/v1" "http://localhost:5000/api/v1"
  ProxyPassReverse "/api/v1" "http://localhost:5000/api/v1"

 <Directory /var/www/agileboard.muhammadzeeshan.dev/react-app/dist>
    AllowOverride All
    Require all granted
    FallbackResource /index.html
  </Directory>

  ErrorLog ${APACHE_LOG_DIR}/agileboard_error.log
  CustomLog ${APACHE_LOG_DIR}/agileboard_access.log combined

 RewriteEngine on
 RewriteCond %{SERVER_NAME} =www.agileboard.muhammadzeeshan.dev [OR]
 RewriteCond %{SERVER_NAME} =agileboard.muhammadzeeshan.dev
 RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]

你需要保持这两行相同

ProxyPass "/api/v1/" "http://localhost:5000/api/v1/"
ProxyPassReverse "/api/v1/" "http://localhost:5000/api/v1/"

在主虚拟主机文件(如 someVirtualhostName.conf)及其 ssl 文件(如 someVirtualhostName-le-ssl.conf)中

我在我的 -le-ssl.conf 文件中的端口 http://localhost:5000 的末尾添加了 /api/v1/ ,它开始正常工作

还要记住,api 前缀(在我的例子中为“/api/v1”)和后端服务器 URL(在我的例子中为 http://localhost:5000)末尾的 / 非常重要

这是我的虚拟主机文件(主文件和 -le-ssl.conf 文件)中 ProxyPass 和 ProxyPassReserve 的最终外观

ProxyPass "/api/v1/" "http://localhost:5000/api/v1/"
ProxyPassReverse "/api/v1/" "http://localhost:5000/api/v1/"
© www.soinside.com 2019 - 2024. All rights reserved.