Nginx二级目录及Flask/Express框架部署单页应用


概要

本篇博客记录 Antd Pro 打包后的程序如何进行部署

  1. 使用 Nginx 容器配置二级目录部署
  2. 使用 Python Flask 部署
  3. 使用 Nodejs Express 部署

起因

以往部署前端打包好后的程序,使用 Nginx 镜像启动运行就可以,昨天需要把程序部署到二级目录下,即访问网站从 "/" 修改为 "/admin" 访问,记录一下需要修改的地方

另外之前用 Flask 写的 API 服务有个后台管理页面,因为很轻量也是附属功能,所以使用 Flask 作为服务,遗留一个刷新页面会 404 问题,一直也没时间研究修复,昨天也一并进行调试,解决了此问题

前端打包要修改的配置

如果是Antd Pro v4框架,那么参照这里设置基准路径

Antd Pro Docs #部署到非根目录

export default {
  // ... some config
  base: "/admin/",
  publicPath: "/admin/",
};

然后进行打包,打包后可见 index.html 内容里会有路径信息

使用 Nginx 配置二级目录部署

首先创建两个目录

  • /opt/subsite 用于存放网页程序
  • /opt/nginx-configs 用来存放 Nginx 配置文件

将前端打包好的 dist 文件夹拷贝到 /opt/subsite 目录下,重命名为 admin

创建 /opt/nginx-configs/default.config

server {
    listen       8080;

    gzip on;
    gzip_min_length 1k;
    gzip_comp_level 9;
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
    gzip_vary on;
    gzip_disable "MSIE [1-6]\.";

    root /usr/share/nginx/html;

    location / {
        # 此处的作用是刷新页面重定向到主页
        try_files $uri $uri/ /admin/index.html;
        index index.html index.htm;
    }
}

启动容器

$ sudo docker run -d --name web-admin --net host -v /opt/subsite/:/usr/share/nginx/html/ -v /opt/nginx-configs/:/etc/nginx/conf.d/ -v /etc/localtime:/etc/localtime:ro --restart=unless-stopped nginx:alpine

使用 Python Flask 来部署

这种方式部署后台只需要一个容器,免去了API服务使用一个容器,管理API的admin后台也用Nginx部署,适合Admin后台较小的项目,如果后台访问者众多,很可能会影响API接口服务的响应速度

话不多说,看示例

# -*- coding: utf-8 -*-
from flask import Flask, send_from_directory
from werkzeug.routing import BaseConverter
import os

class RegexConverter(BaseConverter):
    def __init__(self, url_map,*items):
        super(RegexConverter,self).__init__(url_map)
        self.regex = items[0]

app = Flask(__name__, static_folder='admin_dist')
# Flask 默认没有提供正则路由解析, 此处添加支持
app.url_map.converters['regex'] = RegexConverter

# Serve React App
@app.route('/admin', defaults={'path': ''})
@app.route('/admin/<regex("[\s\S]*"):path>')
def serve(path):
    if path != "" and os.path.exists(os.path.join(app.static_folder, path)):
        return send_from_directory(app.static_folder, path)
    else:
        return send_from_directory(app.static_folder, 'index.html')


if __name__ == '__main__':
    app.run(use_reloader=True, host='0.0.0.0', port=5000, threaded=True)

dist 文件夹改名 admin_dist 放在脚本同级目录,访问:http://[服务器IP]:5000/admin 即可

使用 Nodejs Express 来部署

按照 Flask 的思路可以这样写

const express = require('express')
const path = require('path')
const fs = require('fs')
const app = express()

const DIST_PATH = 'admin_dist'
app.use('/admin', express.static(DIST_PATH))
app.get('/admin/*', function (req, res) {
  let filepath = path.join(__dirname, DIST_PATH, req.params[0])
  if (fs.existsSync(filepath)) {
    res.sendFile(filepath)
  } else {
    res.sendFile(path.join(__dirname, DIST_PATH, 'index.html'));
  }
});

app.listen(5000, '0.0.0.0', () => {
  console.log(`Example app listening on port 5000!`)
})

可以精简一下,Express 设置静态目录已经把资源过滤了一遍,没匹配到的则返回 index.html

const express = require('express')
const path = require('path')
const app = express()

app.use('/admin', express.static(path.join(__dirname, 'admin_dist')));

app.get('/admin/*', function (req, res) {
  res.sendFile(path.join(__dirname, 'admin_dist', 'index.html'));
});

app.listen(5000, '0.0.0.0', () => {
  console.log(`Example app listening on port 5000!`)
})

总结

因为单页应用是 index.html 自己来管理路径,部署到服务器上,刷新导致 404 是因为服务器端没有设置规则匹配子路径,明白了这个原因后,添加路由规则,将 admin 二级目录下的路径做进一步判断,如果是文件,那么返回文件,如果不存在这个文件,那么说明路径是页内的路由,返回 index.html 交给单页应用自己解析处理

本篇博客以 Antd Pro 作为例子,单页应用原理都是一样的,其它框架的单页应用打包设置及部署同理

参考