前端项目开发:代理🚀

FE Project, Proxy

Posted by markzhang on March 17, 2019

上一篇文章大概讲述了js项目开发的整体流程,在这篇文章中我们着重讲解关于代理的部分。

先整体讲一下关于代理的过程:

  • 首先我们先用express(当然也可以是其他的node服务框架)在本地启动一个server服务。
  • 将静态资源的host映射到本地,比如测试环境静态资源网址是m.asset.com,那么可以使用修改host的工具switchhost修改m.asset.com的host为127.0.0.1。
  • 在本地的server服务对请求进行过滤,将请求打到本地webpack打包服务上,获取对应的js资源,然后返回给浏览器。

接下来是实现这个代理服务的不完全代码,但是包括了最核心的部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
let express = require('express'),
    proxy = require('http-proxy-middleware');

let app = express();

if (process.env.PROTOCOL_ENV === 'https') {
    httpsCreate();
} else {
    httpCreate();
}

// 将css和图片等资源修改host代理到测试环境访问
function beforeProxy(urlStr, port) {
    let reg = new RegExp('27.27.27.27');// 这个ip地址是自定义的
    port = port || 9002;
    return function(req, res, next) {
        let rHost = req.host,
            rHost = rHost.replace('m', 'mz');
            isTest = req.headers.host.indexOf('test') === 0,
            url = urlStr;
        if (isTest) {
            // 如果urlStr是不包含ip地址的,那么经过一下这一步是url不会发生变化
            url = url.replace(reg, '$1:' + port);
        }

        return proxy({
            target: 'http://' + rHost,
            router: {
                '^/other_asset': url
            },
            toProxy: true
        }).call(null, req, res, next);
    };
}

// http的启动服务
function httpCreate() {
    app.use('/', (req, res, next) => {
        res.header('Access-Control-Allow-Origin', '*');
        next();
    });

    // css静态资源域名和js一样,通过修改域名的方式
    // 直接请求对应环境的资源,要么是为修改之后的域名绑定了host,要么为这个host做自动映射
    app.use('/other_asset', beforeProxy('/other_asset'));

    // 本地启动的pc js打包服务
    app.use('/js/pc', proxy('http://localhost:9090/js/pc'));

    // 本地启动的h5 js打包服务
    app.use('/js/h5', proxy('http://localhost:3002/js/h5'));

    app.listen(80, 'm.asset.com', (err) => {
        // 这里启动服务的时候需要确保能够找到m.asset.com的host
        // 不然启动会报错
        if (err) {
            console.log(err);
            return;
        }
    });
}

// 代理https下的pc js请求
function proxyPC(req, res, next) {
    proxy({
        target: 'http://localhost:9090',
        ssl: httpsServer,
        secure: false,
        pathRewrite: {
            '^/js/pc': '/js/pc'
        },
        protocolRewrite: 9090
    }).call(null, req, res, next);
}

// 代理https下的h5 js请求
function proxyH5(req, res, next) {
    let _pt = path.join(process.cwd(), 'h5/pages');
    req.url.replace(/\.js\?.*/, '/index.js');
    fs.exist(_pt, exist => {
        if (exist) {
            return proxy({
                target: 'https://localhost:3002',
                ssl: httpsServer,
                secure: false,
                pathRewrite: {
                    '^/js/h5': '/js/h5'
                },
                protocolRewrite: 9090
            }).call(req, res, next);
        } else {
            console.log('h5 js directory does not exist.');
        }
    });
}

// https的启动服务
let fs = require('fs'),
    https = require('https'),
    path = require('path'),
    options = {
        key: fs.readFileSync(path.join(__dirname, '../key.pem')),
        cert: fs.readFileSync(path.join(__dirname, '../cert.pem'))
    };
let httpsServer;
function httpsCreate() {
    // 防止代理请求https地址时,https证书不被信任而被拦截
    // 如果不设置这个环境变量,会出现CERT_UNTRUSTED问题
    process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
    
    app.use('/', (req, res, next) => {
        res.header('Access-Control-Allow-Origin', "*");
        next();
    });

    app.use('/other_asset', beforeProxy('/other_asset'));
    app.use('/js/pc', proxyPC);
    app.use('/js/h5', proxyH5);
    
    httpsServer = https.createServer(options, app);
    httpServer.listen(443, 'm.asset.com', (err) => {
        if (err) {
            console.log(err);
            return;
        }
    });
}