我正在尝试创建一个函数,给定一个路径,将返回用于该路径的Express路由正则表达式。如果我有:
app.get('/a/b/c/:slug(a-z)', (req, res) => {
res.send('ok')
});
我希望能够拥有一个功能:
function getRoute(path) {
// ...
}
getRoute('/a/b/c/whatever') // => '/a/b/c/:slug(a-z0-9*)'
在请求中,我可以访问req.route
处的快速路由对象。它看起来像这样:
{
"path":"/a/b/c/:slug(a-z0-9*)",
"stack":[ ... ],
"methods":{
"get":true
}
}
但我正在寻找一种方法来实现这一点,因为没有请求上下文,因此可以在任何地方调用它。我有app
上下文可用。我可以使用Express应用程序中的某种路由工具吗?
@ jfriend00,这是我正在尝试做的事情。
我有一条路线,让我们说:
app.get('/my-blog/:slug', (req, res) => {
...
});
该网站支持多种语言。所以我们假设有一篇名为“Hello World”的文章。在这种情况下,slug是hello-world
,因此路线是:
/my-blog/hello-world
但是,我也需要一个法语版本。所以路线现在是:
app.get('/my-blog/:slug|/mon-blog/:slug', (req, res) => {
...
});
很酷,所以没问题。现在,该站点是服务器生成的,我正在使用i18n2来翻译所有内容。我还需要翻译网站上的链接。但是,当我在法语页面上看到网址/my-blog/hello-world
时,我需要将其翻译为/mon-blog/bonjour-le-monde
。假设博客帖子有法语和英语slug,并且可以被任何一个检索,所以所有这些URL都工作:
/mon-blog/bonjour-le-monde
/my-blog/bonjour-le-monde
(这永远不会被使用)/mon-blog/hello-world
(这永远不会被使用)/my-blog/hello-world
如果用户的浏览器语言设置为法语,我还需要将到达/my-blog/hello-world
的用户重定向到/mon-blog/bonjour-le-monde
。
我能想到的唯一解决方案就是生成所有网址到法国同行的映射,这有点粗略。如果可能的话,我真的宁愿使用一些有一流支持的东西。感谢您的意见。
答案 0 :(得分:2)
如果我理解您的编辑,原始问题是当用户的浏览器是法语时,如何将链接/my-blog/hello-world
翻译为/mon-blog/bonjour-le-monde
(对于所有其他链接,请执行此操作一页)?
这是一个想法:
而是使用app.get()
,使用一些包装器函数,它们都使用Express注册所需的路由,并创建查找路由以进行匹配的功能。然后,您只需将数据捕获到您自己的数据结构中,而不是使用Express中未记录的内部数据,您可以根据需要使用它。这是一个可用于此目的的模块:
// this is the library that Express uses for converting express
// route definitions to regular expressions
const pathToRegexp = require('path-to-regexp');
// this is an array of arrays or route regular expressions
// the top level array will be in route definition order
// the sub arrays contain an object for each language and must be in a consistent language order
// with English first and then other languages to follow in a consistent order
// For example the sub-array could be routes for English, French, German, Italian in that order
// The object for each language has properties:
// route - original express route string
// keys - keys returned by pathToRegexp
// re - regular expression for this route
// verb - http verb "get", "post", etc... for this route
const allRoutes = [];
// create one of these for each router you are defining matchable routes on
// Then, instead of app.get(...) to define your routes, do it like this:
// const appW = new RouterWrapper(app);
// appW.get(['/my-blog/hello-world', '/mon-blog/bonjour-le-monde'], (req, res) => { ... });
// This wrapper object will both register the route in Express and build a lookup mechanism for mapping routes
class RouterWrapper() {
constructor(router) {
this.router = router;
}
// common function used by all the verbs
_register(routes, verb, ...fn) {
// register route with express
// join all of them together in a regex
let joinedRoute = routes.join("|");
this.router[verb](joinedRoute, ...fn);
// save this set of routes in our master list
allRoutes.push(routes.map(route => {
let obj = {keys: [], route, verb};
obj.re = pathToRegexp(route, obj.keys);
return obj;
}));
}
// pass in an English Route
// returns first route that matches
static getRouteData(englishRoute, languageIndex, verb = "get") {
for (let data of allRoutes) {
// english route is always in position 0 in the array
if (data[0].verb === verb && data[0].re.test(englishRoute)) {
return data[languageIndex];
}
}
// not found
return null;
}
}
// add actual verb methods
["get", "post", "put"].forEach(verb => {
// all verb methods call common function
RouterWrapper.protototype[verb] = function(path, ...fn) {
return this._register(path, verb, ...fn;)
};
});
module.exports = RouterWrapper;
这个想法是你会像这样使用它:
// usage
let RouterWrapper = require('router-wrapper');
let appWrapper = new RouterWrapper(app);
// you define these indexes based on how you order your URLs
const englishIndex = 0;
const frenchIndex = 1;
// define routes
appWrapper.get(['/my-blog/:slug','/mon-blog/:slug'], (req, res) => {
...
});
然后,查找特定的英语路径:
let routeData = RouterWrapper.getRouteData('/my-blog//hello-world', frenchIndex);
console.log(routeData.route); // '/mon-blog/:slug`
根据您的意见,翻译slug将留给您,因为该信息不在路线定义中。
注意事项:
app.use()
。答案 1 :(得分:1)
我应该通过说快递4 hides the router来作为序言。
此解决方案依赖于express的私有内部。谨慎行事。
快递路由器内部维护一个层的数组。处理请求时,它会逐个查看这些层,并测试它们是否与请求路径匹配。
您可以使用类似于以下的方法来模仿express的内部行为。
function getRoutePath(path) {
var stack = app._router.stack;
for (var i = 0; i < stack.length; i++) {
if (stack[i].route && stack[i].match(path)) {
return stack[i].route.path;
}
}
}
这将获取路径并返回匹配的第一条路线的模式。
app.get('/foo/:bar', (req, res) => {
res.send('ok')
});
getRoutePath('/foo/gotcha'); // -> "/foo/:bar"