3

我正在寻找一种方法,在 Cowboy 中,将任意路径(存储在数据库中)映射到特定的博客文章。

那就是:我有几千篇博客文章,每个都可以通过几个名称访问,例如规范 URL(例如/post/42)、一些别名(例如/2013/11/25/erlang-rocks)、历史位置(例如/path-on-old-blog/12345)等。

我知道我可以简单地使用一条包罗万象的路线:

{ "/[...]", catch_all_handler, [] },

...然后在数据库中查找路径,但我正在考虑从数据库创建路由,如下所示:

Posts = posts:all(),
Paths = [get_handlers_for_post(P) || P <- Posts],
Routes = lists:flatten(Paths),

get_handler_for_post(P) ->
    % Generate a list of paths with IDs from the database.
    % Return something that looks like this:
    %  [{"/canonical/1", post_handler, [1]},
    %   {"/first-alias", post_handler, [1]}].
% TODO: code goes here...

即:将所有可能的路径放入路由器中,指向同一个处理程序,每条路径都有帖子的ID。

问题是:这合理吗?牛仔支持多少条路线?

4

2 回答 2

4

你可以这样做,但没有必要。Cowboy 在路由中有非常有效的模式匹配语法。让我们以您在示例中给出的路线为例

[{"/canonical/1", post_handler, [1]},
 {"/first-alias", post_handler, [1]}].

第一个 url 有一个可选的附加路径。在牛仔中,您可以将这两条路线表示为

"/:first/[:second]"

这匹配/canonical/1以及/first-alias

first 和 second 都是参数化的,它们可以采用任何值。方括号:second表示这是可选的。上述模式将匹配您提供的两条路线。

那么如何在路由处理程序中实际访问这些参数呢?

真的很简单。Cowboy 在 cowboy_req 模块中提供了一个绑定方法,您可以像这样从那里访问您的 url 的参数

cowboy_req:binding(first,Req)

如果是您的第一个 url,这将返回{<<"canonical">>,Req}.

请注意,参数是一个atom. 使用参数和可选参数,您应该能够匹配您的整个 url 集合。

在此处阅读有关路由的更多信息

更多解释

据我了解,您有数千篇不同的博客文章,并且它们的网址并不一致。我建议不要动态创建路由,而是找到一致的 url 模式并将它们分组到一个路由中。回退在牛仔中自动发生。如果它与模式不匹配,它会查找另一个,依此类推。

例如

\:a\:b

将匹配

\hello\man, hello\world,\hello\slash\

不匹配hello\man\world

\:a\:b\[:c]

将匹配\hello\man, hello\world,hello\man\world

路线数量没有硬性限制。您可以根据需要拥有任意数量。

于 2014-01-02T16:42:31.300 回答
0

很久以前问过,还是很有趣的。

不,我不认为生成的 Cowboy 路由规则是查找大量非结构化路径的有效方法。

产生的调度规则cowboy_router:compile/1是元组、列表和二进制文件的结构,如下所示:

[{'_',[],
      [{[<<"canonical">>,<<"1">>],[],post_handler,[1]},
       {[<<"first-alias">>],[],post_handler,[1]}]}]

路由是这种结构中的线性搜索。它被复制到每个请求处理程序进程,因此如果它非常大,则复制每个请求都会产生很大的开销。

在最新版本的 Cowboy 中,路线可以存储在 中persistent_term,从而消除了复制。尽管如此,它仍然是线性搜索。

对于大量非结构化路径,我相信 ETS 表查找会更有效,因为它是作为哈希表实现的。

由于您正在考虑代码生成,我想提及的另一个选项是生成一个 Erlang 模块,其中包含一个执行查找的函数。这消除了复制,并且可以从模式匹配的编译器优化中受益。

%% Generated module
-module(blog_path_aliases).
-export([lookup/1]).
lookup(<<"/2013/11/25/erlang-rocks">>) -> 42;
lookup(<<"/path-on-old-blog/12345">>) -> 42;
lookup(<<"/some-other/path">>) -> 123;
...
于 2020-12-22T11:26:22.050 回答