React 路由器路径、SNICallback、POST、GET
React Router Paths, SNICallback, POST, GET
我已经构建了以下 Express 应用程序以从单个服务器使用 HTTPS 服务多个站点:
// strict
'use strict';
// vars
const tls = require('tls');
var fs = require('fs');
var http = require('http');
var https = require('https');
var express = require('express');
var vhost = require('vhost');
var forceSSL = require('express-force-ssl');
var cors = require('cors');
var app = express();
var server;
var secureServer;
// always force HTTP -> HTTPS redirect
app.use(forceSSL);
// allow cross origin
app.use(cors());
// Express settings (main switch logic - each host, and each static directory)
var siteOne = vhost('siteone.com', express.static('../siteone/dist'));
var wwwSiteOne = vhost('www.siteone.com', express.static('../siteone/dist'));
var siteTwo = vhost('sitetwo.com', express.static('../sitetwo/dist'));
var wwwSiteTwo = vhost('www.sitetwo.com', express.static('../sitetwo/dist'));
app.use(cors()); // allow cross origin
app.use(siteOne); // all hosts defined above
app.use(wwwSiteOne);
app.use(siteTwo);
app.use(wwwSiteTwo);
app.use(function(req, res, next) { // 404 page as last "use" call
res.status(404).send('404 page :(');
});
// HTTP Server - http.createServer is enough for our HTTP needs
server = http.createServer(app);
server.listen(8080); // router is set to forward port 80 (http requests) to port 8080
// HTTPS Server - use virtual host to redirect with secureserver
var secureContext = {
'siteone.com': tls.createSecureContext({
key: fs.readFileSync('../../sslcert/siteone/privkey.pem', 'utf8'),
cert: fs.readFileSync('../../sslcert/siteone/fullchain.pem', 'utf8'),
ca: fs.readFileSync('../../sslcert/siteone/chain.pem', 'utf8'),
}),
'www.siteone.com': tls.createSecureContext({
key: fs.readFileSync('../../sslcert/siteone/privkey.pem', 'utf8'),
cert: fs.readFileSync('../../sslcert/siteone/fullchain.pem', 'utf8'),
ca: fs.readFileSync('../../sslcert/siteone/chain.pem', 'utf8'),
}),
'sitetwo.com': tls.createSecureContext({
key: fs.readFileSync('../../sslcert/sitetwo/privkey.pem', 'utf8'),
cert: fs.readFileSync('../../sslcert/sitetwo/fullchain.pem', 'utf8'),
ca: fs.readFileSync('../../sslcert/sitetwo/chain.pem', 'utf8'),
}),
'www.sitetwo.com': tls.createSecureContext({
key: fs.readFileSync('../../sslcert/sitetwo/privkey.pem', 'utf8'),
cert: fs.readFileSync('../../sslcert/sitetwo/fullchain.pem', 'utf8'),
ca: fs.readFileSync('../../sslcert/sitetwo/chain.pem', 'utf8'),
}),
}
try {
var options = {
SNICallback: function (domain, cb) {
if (secureContext[domain]) {
if (cb) {
cb(null, secureContext[domain]);
} else {
// compatibility for older versions of node
return secureContext[domain];
}
} else {
console.log('Doing nothing. Domain requsted: ' + domain);
}
},
// must list a default key and cert because required by tls.createServer()
key: fs.readFileSync('../../sslcert/siteone/privkey.pem'),
cert: fs.readFileSync('../../sslcert/siteone/fullchain.pem'),
}
secureServer = https.createServer(options, app);
secureServer.listen(8043); // router is set to forward port 443 (https requests) to port 8043
} catch (err){
console.error(err.message);
console.error(err.stack);
}
到目前为止,我 运行 遇到了 POST、GET 和反应路由器 (v4) 路径的问题。例如,只要 用户从主页开始,带有 React 路由器的单页站点就可以对每个 link 起作用,但是如果用户提供 link直接在 url 栏中(例如 siteone。com/somecoolpath)我从网站开关中获取 404 页面。
同样,我有一个来自网站的 POST,用于将用户的电子邮件添加到位于 url 路径 /add_email 上的数据库...我总是收到 404我发邮件...
所有这些路径都在它们自己的应用程序上工作,但当我通过这个 SNICallback 开关提供服务时就不行了。
我尝试过但没有奏效的事情:
虚拟主机中的通配符:
var reactRouterSite = vhost('siteone.com*', express.static('../siteone/dist'));
重写 SNI 回调中的域:
SNICallback: function (domain, cb) {
if (domain.includes('siteone')) { // any request from siteone
domain = 'siteone.com'
}
...
最后一个 app.use() 语句中的最后一个 switch() 语句:
app.use(function(req, res, next) {
console.log(req);
switch (req.url) {
case '/somecoolpath':
return siteOne;
break;
default:
res.status(404).send('404 page :(');
}
});
我花了几个小时四处寻找可能的解决方案,但甚至还没有找到一个带有 SNICallback 的反应路由器的案例……我可以尝试什么想法?或者有更简单的解决方案吗?
所有站点都托管在其他节点实例中,端口 8081、8082 等上的快速应用程序。对于所有站点,根页面通过此 'switch' 按预期加载,只是那些 GET/POST urls 和 copy/pasting 路由器 urls 不工作。
您可以做的一件事是摆脱 vhosts 中间件,只创建一个 app.get('*')
路由并自己执行逻辑检查 req.hostname
,而不是使用静态目录,呈现一个模板。我已经用多个域和多个 React 应用程序完成了此操作。
根据 dzm 的回复,我最终确实取出了 vhost
中间件,而是使用了 app.get('*' ...)
路由,但我发现代理最适合用于我的需求,用过the http-proxy package。经过一些测试,反应路由器路径可以直接在浏览器中 copy/pasted 并按预期工作,并且 GET/POST 方法也可以工作,只要 app.get()
和 app.post()
路径也存在于网站开关中。
替换 vhosts
/ app.use()
部分,同时保留代码的其他部分,即 SNICallback
和 secureContext
不变,解决方案如下所示:
// set up proxy server
var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer({}); // leave options empty --> custom logic below in app.get('*' ...)
// GET paths
app.get('*', function(req, res) {
// custom logic to handle and proxy the request
console.log(req.hostname);
switch (req.hostname) {
case 'siteone.com':
proxy.web(req, res, { target: 'http://127.0.0.1:8081' }); // different node server running on local port 8081
break;
case 'sitetwo.com':
proxy.web(req, res, { target: 'http://127.0.0.1:8082' }); // different node server running on local port 8082
break;
default:
res.status(404).send('404 page :(');
break;
}
});
// POST paths (for siteone)
app.post('/new_email', function(req, res) {
proxy.web(req, res, { target: 'http://127.0.0.1:8081' }); // this post path must also be written on the index.js of siteone.com
});
我已经构建了以下 Express 应用程序以从单个服务器使用 HTTPS 服务多个站点:
// strict
'use strict';
// vars
const tls = require('tls');
var fs = require('fs');
var http = require('http');
var https = require('https');
var express = require('express');
var vhost = require('vhost');
var forceSSL = require('express-force-ssl');
var cors = require('cors');
var app = express();
var server;
var secureServer;
// always force HTTP -> HTTPS redirect
app.use(forceSSL);
// allow cross origin
app.use(cors());
// Express settings (main switch logic - each host, and each static directory)
var siteOne = vhost('siteone.com', express.static('../siteone/dist'));
var wwwSiteOne = vhost('www.siteone.com', express.static('../siteone/dist'));
var siteTwo = vhost('sitetwo.com', express.static('../sitetwo/dist'));
var wwwSiteTwo = vhost('www.sitetwo.com', express.static('../sitetwo/dist'));
app.use(cors()); // allow cross origin
app.use(siteOne); // all hosts defined above
app.use(wwwSiteOne);
app.use(siteTwo);
app.use(wwwSiteTwo);
app.use(function(req, res, next) { // 404 page as last "use" call
res.status(404).send('404 page :(');
});
// HTTP Server - http.createServer is enough for our HTTP needs
server = http.createServer(app);
server.listen(8080); // router is set to forward port 80 (http requests) to port 8080
// HTTPS Server - use virtual host to redirect with secureserver
var secureContext = {
'siteone.com': tls.createSecureContext({
key: fs.readFileSync('../../sslcert/siteone/privkey.pem', 'utf8'),
cert: fs.readFileSync('../../sslcert/siteone/fullchain.pem', 'utf8'),
ca: fs.readFileSync('../../sslcert/siteone/chain.pem', 'utf8'),
}),
'www.siteone.com': tls.createSecureContext({
key: fs.readFileSync('../../sslcert/siteone/privkey.pem', 'utf8'),
cert: fs.readFileSync('../../sslcert/siteone/fullchain.pem', 'utf8'),
ca: fs.readFileSync('../../sslcert/siteone/chain.pem', 'utf8'),
}),
'sitetwo.com': tls.createSecureContext({
key: fs.readFileSync('../../sslcert/sitetwo/privkey.pem', 'utf8'),
cert: fs.readFileSync('../../sslcert/sitetwo/fullchain.pem', 'utf8'),
ca: fs.readFileSync('../../sslcert/sitetwo/chain.pem', 'utf8'),
}),
'www.sitetwo.com': tls.createSecureContext({
key: fs.readFileSync('../../sslcert/sitetwo/privkey.pem', 'utf8'),
cert: fs.readFileSync('../../sslcert/sitetwo/fullchain.pem', 'utf8'),
ca: fs.readFileSync('../../sslcert/sitetwo/chain.pem', 'utf8'),
}),
}
try {
var options = {
SNICallback: function (domain, cb) {
if (secureContext[domain]) {
if (cb) {
cb(null, secureContext[domain]);
} else {
// compatibility for older versions of node
return secureContext[domain];
}
} else {
console.log('Doing nothing. Domain requsted: ' + domain);
}
},
// must list a default key and cert because required by tls.createServer()
key: fs.readFileSync('../../sslcert/siteone/privkey.pem'),
cert: fs.readFileSync('../../sslcert/siteone/fullchain.pem'),
}
secureServer = https.createServer(options, app);
secureServer.listen(8043); // router is set to forward port 443 (https requests) to port 8043
} catch (err){
console.error(err.message);
console.error(err.stack);
}
到目前为止,我 运行 遇到了 POST、GET 和反应路由器 (v4) 路径的问题。例如,只要 用户从主页开始,带有 React 路由器的单页站点就可以对每个 link 起作用,但是如果用户提供 link直接在 url 栏中(例如 siteone。com/somecoolpath)我从网站开关中获取 404 页面。
同样,我有一个来自网站的 POST,用于将用户的电子邮件添加到位于 url 路径 /add_email 上的数据库...我总是收到 404我发邮件...
所有这些路径都在它们自己的应用程序上工作,但当我通过这个 SNICallback 开关提供服务时就不行了。
我尝试过但没有奏效的事情:
虚拟主机中的通配符:
var reactRouterSite = vhost('siteone.com*', express.static('../siteone/dist'));
重写 SNI 回调中的域:
SNICallback: function (domain, cb) {
if (domain.includes('siteone')) { // any request from siteone
domain = 'siteone.com'
}
...
最后一个 app.use() 语句中的最后一个 switch() 语句:
app.use(function(req, res, next) {
console.log(req);
switch (req.url) {
case '/somecoolpath':
return siteOne;
break;
default:
res.status(404).send('404 page :(');
}
});
我花了几个小时四处寻找可能的解决方案,但甚至还没有找到一个带有 SNICallback 的反应路由器的案例……我可以尝试什么想法?或者有更简单的解决方案吗?
所有站点都托管在其他节点实例中,端口 8081、8082 等上的快速应用程序。对于所有站点,根页面通过此 'switch' 按预期加载,只是那些 GET/POST urls 和 copy/pasting 路由器 urls 不工作。
您可以做的一件事是摆脱 vhosts 中间件,只创建一个 app.get('*')
路由并自己执行逻辑检查 req.hostname
,而不是使用静态目录,呈现一个模板。我已经用多个域和多个 React 应用程序完成了此操作。
根据 dzm 的回复,我最终确实取出了 vhost
中间件,而是使用了 app.get('*' ...)
路由,但我发现代理最适合用于我的需求,用过the http-proxy package。经过一些测试,反应路由器路径可以直接在浏览器中 copy/pasted 并按预期工作,并且 GET/POST 方法也可以工作,只要 app.get()
和 app.post()
路径也存在于网站开关中。
替换 vhosts
/ app.use()
部分,同时保留代码的其他部分,即 SNICallback
和 secureContext
不变,解决方案如下所示:
// set up proxy server
var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer({}); // leave options empty --> custom logic below in app.get('*' ...)
// GET paths
app.get('*', function(req, res) {
// custom logic to handle and proxy the request
console.log(req.hostname);
switch (req.hostname) {
case 'siteone.com':
proxy.web(req, res, { target: 'http://127.0.0.1:8081' }); // different node server running on local port 8081
break;
case 'sitetwo.com':
proxy.web(req, res, { target: 'http://127.0.0.1:8082' }); // different node server running on local port 8082
break;
default:
res.status(404).send('404 page :(');
break;
}
});
// POST paths (for siteone)
app.post('/new_email', function(req, res) {
proxy.web(req, res, { target: 'http://127.0.0.1:8081' }); // this post path must also be written on the index.js of siteone.com
});