在 Netlify 上部署时使用 Express 公开 API 路由

Posted

技术标签:

【中文标题】在 Netlify 上部署时使用 Express 公开 API 路由【英文标题】:Expose API routes with Express when deployed on Netlify 【发布时间】:2018-05-14 14:46:44 【问题描述】:

我使用create-react-app 创建了一个站点,我在./client/build 中构建并使用express 提供服务。

使用express,我还设置了 2 个在 dev (localhost) 中运行良好的基本 API 路由。

在netlify 上部署什么?

我用react-scripts构建客户端 然后我手动将环境设置为 production 以使用webpack在本地构建服务器 最后,我将代码推送到 GitLab 存储库,它会自动触发部署。

使用 Netlify deploy 的设置:

Build command:         Not set
Publish directory:     client/build

问题

当我尝试使用我的 API 路由之一时,我收到错误 404 ...

项目

我的项目结构是这样的:

|   package.json
|   server.js
|   webpack.config.js
+---API
|       dbHandler.js
|       routesHandler.js
+---client
|   |   package.json
|   +---src
|   |   |   App.js
|   |   |   ...
|   +---node_modules
|   +---public
|   |       index.html
|   |       manifest.json
|   |       _redirects
|   \---build
|       |   index.html
|       |   manifest.json
|       |   _redirects
|       |   asset-manifest.json
|       |   favicon_rings.ico
|       |   service-worker.js
|       \---static
|           +---media
|           |       ...
|           +---js
|           |       main.1e7ccdbf.js
|           |       main.1e7ccdbf.js.map
|           \---css
|                   main.fc8f2d26.css
|                   main.fc8f2d26.css.map
+---public
|       _redirects
\---bundles
        bundle.js 

代码

./package.json 中提取:

"main": "server.js",
"homepage": "https://custom.netlify.com",
"scripts": 
  "start": "node server.js",
  "build": "webpack --config webpack.config.js",
  "postinstall": "npm run build",
  "start:dev": "nodemon -e js --exec babel-node -- ./server.js"

./client/package.json

"homepage": "https://custom.netlify.com",
"scripts": 
  "start": "react-scripts start",
  "build": "react-scripts build && cp build/index.html build/404.html",
  "test": "react-scripts test --env=jsdom",
  "eject": "react-scripts eject"
,
"proxy": "http://localhost:5000"

./client/build/_redirects./public/_redirects

# Redirects from what the browser requests to what we serve
/*  /index.html  200

./server.js

import webpackDevMiddleware from "webpack-dev-middleware";
import webpackHotMiddleware from "webpack-hot-middleware";
import routesHandler from "./API/routesHandler";
import config from "./webpack.config";
import bodyParser from "body-parser";
import webpack from "webpack";
import express from "express";
import path from "path";
const port = process.env.PORT || 5000;
const ExpressServer = express();
const CLI_BUILD_DIR = path.join(__dirname, "client/build");
const HTML_BUNDLE = path.join(CLI_BUILD_DIR, "index.html");
const compiler = webpack(config);
const isDevelopment = process.env.NODE_ENV === "development";

ExpressServer.use(bodyParser.urlencoded( extended: true ));
ExpressServer.use(bodyParser.json());
ExpressServer.use(function(req, res, next) 
  res.header("Access-Control-Allow-Origin", "*");
  res.header(
    "Access-Control-Allow-Headers",
    "Origin, X-Requested-With, Content-Type, Accept"
  );
  next();
);
if (isDevelopment) 
  ExpressServer.use(
    webpackDevMiddleware(compiler, 
      publicPath: config.output.publicPath
    )
  );
  ExpressServer.use(webpackHotMiddleware(compiler));
 else 
  // Serve static files from the React app
  ExpressServer.use(express.static(CLI_BUILD_DIR));


// Define API routes
var router = express.Router();
router.post("/register", routesHandler.register);
router.post("/login", routesHandler.login);
// Put all API endpoints under '/API'
ExpressServer.use("/API", router);
// All other routes will be directed to the main page of user interface
ExpressServer.get("/", (req, res) => res.sendFile(HTML_BUNDLE));
ExpressServer.get("*", (req, res) => res.redirect(301, "/"));

// Start the server
ExpressServer.listen(port, function() 
  console.log("Express server listening on http://localhost:" + port);
);

./webpack.config.js

const path = require('path');
const webpack = require('webpack');

process.env.NODE_ENV = process.env.NODE_ENV || 'development';

module.exports = 
  entry: [
    'webpack-hot-middleware/client',
    './API/routesHandler.js',
    './API/dbHandler.js',
    './server.js'
  ],
  externals: 
    jquery: 'jQuery',
    'react/addons': 'react',
    'react/lib/ExecutionEnvironment': 'react',
    'react/lib/ReactContext': 'react',
  ,
  output: 
    path: path.resolve(__dirname, 'bundles'),
    filename: 'bundle.js',
    publicPath: '/public',
    sourceMapFilename: 'bundle.map',
  ,
  devtool: process.env.NODE_ENV === 'production'
    ? undefined : 'cheap-module-eval-source-map',
  resolve: 
    modules: ['node_modules', './client/build', './API', '.'],
    extensions: ['.js', '.jsx', '.css'],
  ,
  module: 
    rules: [
      
        test: /(\.js$|\.jsx$)/,
        exclude: /(node_modules|bower_components)/,
        use: [
          
            loader: 'babel-loader',
            options: 
              presets: ['react', 'es2015', 'stage-0', 'airbnb'],
            ,
          ,
        ],
      ,
      
        test: /(\.css$|\.scss$)/,
        use: ['style-loader', 'css-loader', 'sass-loader'],
      ,
      
        test: /\.(eot|svg|ttf|woff|woff2|png|jpg|jpeg|gif)(\?v=\d+\.\d+\.\d+)?$/,
        use: 
          loader: 'file-loader',
          options: 
            name: '[name].[ext]'
          
        
      
    ],
  ,
  node: 
    console: false,
    fs: 'empty',
    fsevents: 'empty'
  ,
  plugins: [
    new webpack.IgnorePlugin(/fs/)],
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin(),
    new webpack.ProvidePlugin(
      $: 'jquery',
      jQuery: 'jquery',
    ),
    new webpack.optimize.UglifyJsPlugin(
      sourceMap: true,
      minimize: true,
      compressor: 
        warnings: false,
      ,
    ),
    new webpack.DefinePlugin(
      'process.env': 
        NODE_ENV: JSON.stringify(process.env.NODE_ENV)
      ,
    ),
  ],
;

【问题讨论】:

【参考方案1】:

Netlify 用于静态文件托管 - 您不能将 Express 应用程序部署到它,只能部署您的前端。您需要为 API 托管使用不同的服务(例如 DigitalOcean 或 Heroku)。如果您想使用他们的 CDN 进行响应缓存,您可以通过 Netlify 使用 redirects 将流量路由到该 API 服务器。

编辑:这不再完全正确 - Netlify 现在也有 support for deploying AWS Lambda functions,允许 javascript 和 Go 在服务器端运行。

【讨论】:

是的,我负责管理 Netlify 的技术支持团队,我也会这么说。具体来说,您可能希望使用该文章的本节中特别描述的重定向代理形式:netlify.com/docs/redirects/#proxying - 这使您的访问者可以访问一组一致的 URL,但该后端将向他们透明地提供一些路径(与 Netlify 的相同 SSL 连接,由 heroku/etc 提供的内容)。我们将遵循您在该远程服务上设置的 Cacne-Control 标头 - 所以请明智地选择 :) @fool:啊,为额外的信息欢呼:) 和 谢谢你的回答,我觉得错过了这个有点傻,但确实它在重定向服务于其他地方托管的后端时工作得更好!谢谢你的信息! ;) @JoeClay 参考 Netlify 的新函数功能可能值得更新您的答案。【参考方案2】:

随着今年早些时候 Netlify 增加了对 AWS Lamdba 功能的支持,现在这实际上是完全可能的。这里有一些资源可供现在发现此问题的人使用。

https://www.netlify.com/docs/functions/

https://www.netlify.com/blog/2018/09/13/how-to-run-express.js-apps-with-netlify-functions/

【讨论】:

以上是关于在 Netlify 上部署时使用 Express 公开 API 路由的主要内容,如果未能解决你的问题,请参考以下文章

如何将 express.js 服务器部署到 Netlify

跨域会话 Cookie(Heroku 上的 Express API + Netlify 上的 React App)

在 Netlify 上部署 gatsby 站点时响应错误的大小图像

使用 Nextjs 在 Netlify 上部署 Storybook

Netlify 上的 Ruby 版本

尝试访问部署在 Netlify 上的站点时出现“找不到页面”