当前位置: 首页 > 工具软件 > Cowboy > 使用案例 >

Cowboy 用户指南 (七) - Routing

丌官凯康
2023-12-01

路由

Cowboy 默认不执行任何操作.

要使Cowboy有用,需要将URI映射到负责处理请求的Erlang模块。这被称为路由。

URI,统一资源标志符(Uniform Resource Identifier, URI),表示的是web上每一种可用的资源,如 HTML文档、图像、视频片段、程序等都由一个URI进行标识的。

Cowboy使用以下算法路由请求:

  • 如果没有配置的主机与请求URI匹配,则返回400响应。
  • 否则,将使用第一个配置的和请求URI匹配的主机。只考虑为该主机配置的路径。
  • 如果上一步中找到的配置路径与请求URI不匹配,则返回404响应。
  • 否则,处理程序及其初始状态将被添加到环境中,请求将继续被处理。

注意:可能会遇到这样的情况:两台主机匹配一个请求URI,但只有第二台主机上的路径匹配请求URI。在本例中,预期的结果是一个404响应,因为在路由过程中使用的唯一路径是与请求URI匹配的第一个配置主机的路径。

路由需要在Cowboy使用之前先编译。编译的结果是分派规则(dispatch rules)。

下面可能翻译得不太好 请各位结合例子就比较好理解了

语法

路由的一般结构定义如下。

Routes = [Host1, Host2, ... HostN].

每个主机都包含该主机的匹配规则HostMatch、可选约束Constraints以及一个path组件的路由列表PathsList

Host1 = {HostMatch, PathsList}.
Host2 = {HostMatch, Constraints, PathsList}.

path组件的路由列表的定义类似于主机列表。

PathsList = [Path1, Path2, ... PathN].

最后,每个路径都包含该路径的匹配规则PathMatch和可选约束Constraints,并为我们提供了要使用的处理程序模块Handler及其初始状态InitialState

Path1 = {PathMatch, Handler, InitialState}.
Path2 = {PathMatch, Constraints, Handler, InitialState}.

继续阅读,了解更多关于匹配语法和可选约束的信息.

匹配语法

匹配语法用于将主机名和路径与它们各自的处理程序关联起来。

主机和路径的匹配语法是相同的,只是有一些细微差别。实际上,段分隔符是不同的,主机从最后一个段开始匹配到第一个段。所有的例子都具有主机和路径匹配规则,并解释遇到时的差异。

除去我们将在本节末尾解释的特殊值,最简单的匹配值是一个主机或一条路径。它可以以string()或binary()的形式给出。

PathMatch1 = "/".
PathMatch2 = "/path/to/resource".

HostMatch1 = "cowboy.example.org".

如您所见,以这种方式定义的所有路径都必须以" / "开头。请注意,就路由而言,这两条路径是相同的。

PathMatch2 = "/path/to/resource".
PathMatch3 = "/path/to/resource/".

带或不带尾随点的主机等价于路由。类似地,带和不带前导点的主机也是相等的。

HostMatch1 = "cowboy.example.org".
HostMatch2 = "cowboy.example.org.".
HostMatch3 = ".cowboy.example.org".

可以提取主机和路径的片段,并将值存储在Req对象中以供以后使用。我们称这种值为绑定。

绑定的语法非常简单。一个以:字符开头的片段 表示 在片段结束之前后面是段值将存储在其中的绑定的名称。(可参考下面的例子)

PathMatch = "/hats/:name/prices".
HostMatch = ":subdomain.example.org".

如果这两个在路由时匹配,那么您将定义两个绑定,subdomain name,每个都包含定义它们的片段值。例如,URL http://test.example.org/hats/wild_cowboy_legendary/prices 将导致将值test绑定到subdomain,并将值wild_cowboy_legendary绑定到名称name。稍后可以使用cowboy_req:binding/{2,3}来检索它们。绑定名称必须以原子(atom)的形式给出。

有一个特殊的绑定名可以用来模拟Erlang中的下划线变量。任何针对 _  绑定的匹配都将成功,但是数据将被丢弃。这对于一次匹配多个域名特别有用。

HostMatch = "ninenines.:_".

类似地,也可以有可选的片段。括号内的内容都是可选的。

PathMatch = "/hats/[page/:number]".
HostMatch = "[www.]ninenines.eu".

你也可以有堆叠的可选片段。

PathMatch = "/hats/[page/[:number]]".

虽然Cowboy不会拒绝路由中的多个括号,但如果路由未被指定,则该行为可能是未定义的。例如,这个路径需要约束来确定什么是章节,什么是页面,因为它们都是可选的:

PathMatch = "/book/[:chapter]/[:page]".

您可以使用[…]检索主机或路径的其余部分。在主机的情况下,它将匹配前面的任何内容,在路径的情况下,它将匹配前面匹配的片段之后的任何内容。它是可选段的特殊情况,因为它可以有0个、一个或多个片段。然后,你可以分别使用cowboy_req:host_info/1和cowboy_req:path_info/1找到片段。它们将表示为一个片段列表。

PathMatch = "/hats/[...]".
HostMatch = "[...]ninenines.eu".

如果一个绑定在路由规则中出现两次,那么只有当它们共享相同的值时,匹配才会成功。这复制了Erlang模式匹配行为。

PathMatch = "/hats/:name/:name".

当存在一个可选的片段时,也是如此。在这种情况下,这两个值必须是相同的片段才是可用的。

PathMatch = "/hats/:name/[:name]".

如果一个绑定在主机和路径中都定义,那么它们也必须共享相同的值。

PathMatch = "/:user/[...]".
HostMatch = ":user.github.com".

最后,可以使用两个特殊的匹配值。第一个是atom '_',它将匹配任何主机或路径。

PathMatch = '_'.
HostMatch = '_'.

第二个是特殊的主机匹配“*”,它将匹配通配符路径,通常与OPTIONS方法一起使用。

HostMatch = "*".

约束

匹配完成后,可以根据一组约束测试结果绑定。约束只在定义绑定时测试。它们按照您定义的顺序运行。只有他们都成功,匹配才会成功。如果匹配失败,Cowboy将尝试列表中的下一条路线。

约束使用的格式与cowboy_req中的匹配函数相同:它们作为字段列表提供,这些字段可能有一个或多个约束。当路由器接受相同的格式时,它会跳过没有约束的字段,也会忽略默认值(如果有的话)。

阅读更多关于约束constraints的内容

编译

在Cowboy使用它们之前,必须先把路线编译好。编译步骤规范化了路由,以简化代码并加快执行速度,但最终还是会逐个查找路由。更快的编译策略可以是直接编译到Erlang代码的路由,但需要更重的依赖性。

要编译路由,只需调用适当的函数:

Dispatch = cowboy_router:compile([
    %% {HostMatch, list({PathMatch, Handler, InitialState})}
    {'_', [{'_', my_handler, #{}}]}
]),
%% Name, TransOpts, ProtoOpts
cowboy:start_clear(my_http_listener,
    [{port, 8080}],
    #{env => #{dispatch => Dispatch}}
).

使用 persistent_term

路由可以存储在从Erlang/OTP 21.2开始支持的persistent_term中。当有大量路由时,这可能会提高性能。

要使用这个功能,你需要编译路由,将它们存储在persistent_term中,然后通知Cowboy:

Dispatch = cowboy_router:compile([
    {'_', [{'_', my_handler, #{}}]}
]),
persistent_term:put(my_app_dispatch, Dispatch),
cowboy:start_clear(my_http_listener,
    [{port, 8080}],
    #{env => #{dispatch => {persistent_term, my_app_dispatch}}}
).

在线更新

你可以使用cowboy:set_env/3函数来更新路由使用的分派列表。这将适用于监听器接受的所有新连接:

Dispatch = cowboy_router:compile(Routes),
cowboy:set_env(my_http_listener, dispatch, Dispatch).

注意,在更新之前,您需要重新编译路由。

当使用persistent_term时,不需要调用这个函数,只需将新路由放入存储中

 类似资料: