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

Cowboy 用户指南 (十二) - Request details

简学文
2023-12-01

请求对象

Req对象是一个变量,用于获取关于请求的信息、读取请求体或发送响应。

在面向对象的意义上,它并不是一个真正的对象。它是一个简单的映射,可以在调用cowboy_req模块的函数时直接访问或使用。

Req对象是几个不同章节的主题。在本章中,我们将了解Req对象,并了解如何检索有关请求的信息。

Direct access

Req映射包含许多文档化的字段,可以直接访问这些字段。它们是直接映射到HTTP的字段:

        使用的HTTP版本;

        有效的URI组件schemehostportpath 和 qs;

        请求头;

        连接对端地址和端口;

        和TLS证书(如适用)。

请注意,version字段可用于确定连接是否使用HTTP/2。

要访问一个字段,只需在函数头中进行匹配。下面的示例在方法为GET时发送一个简单的“Hello world!”响应,否则发送405错误。

init(Req0=#{method := <<"GET">>}, State) ->
    Req = cowboy_req:reply(200, #{
        <<"content-type">> => <<"text/plain">>
    }, <<"Hello world!">>, Req0),
    {ok, Req, State};
init(Req0, State) ->
    Req = cowboy_req:reply(405, #{
        <<"allow">> => <<"GET">>
    }, Req0),
    {ok, Req, State}.

任何其他字段都是内部的,不应该被访问。它们可能在未来的版本(包括维护版本)中发生变化,而不另行通知。

允许修改Req对象,但是在修改现有字段时必须格外小心。但是,您可以根据需要添加任意数量的新字段。只要确保命名空间字段名,这样就不会与未来的Cowboy更新或第三方项目发生冲突。

cowboy_req接口的介绍

cowboy_req模块中的函数提供了对请求信息的访问,还提供了处理HTTP请求时常见的各种操作。

以动词开头的所有功能都表示一个动作。其他函数只是返回相应的值(有时确实需要构建该值,但操作的成本等同于检索值)。

一些cowboy_req函数返回更新后的Req对象。它们是read、reply、set和delete函数。尽管忽略返回的请求不会导致其中一些请求的错误行为,但强烈建议始终保留并使用最后一个返回的请求对象。cowboy_req的手册详细说明了这些函数以及对Req对象所做的修改。

对cowboy_req的一些调用有副作用。这就是read和reply函数的情况。当函数被调用时,Cowboy立即读取请求主体或回复。

如果出了问题,所有的函数都会崩溃。通常不需要捕捉这些错误,Cowboy将根据崩溃发生的位置发送适当的4xx或5xx响应。

请求方法

可以直接检索请求方法:

#{method := Method} = Req.

或者使用函数:

Method = cowboy_req:method(Req).

该方法是区分大小写的二进制字符串。标准方法包括GET、HEAD、OPTIONS、PATCH、POST、PUT或DELETE。

HTTP 版本

HTTP版本只是信息。这并不表明客户端很好地或完全地实现了协议。

通常不需要根据HTTP版本改变行为:Cowboy已经为您做了。

但在某些情况下,它是有用的。例如,人们可能想重定向HTTP/1.1客户端而使用Websocket,而HTTP/2客户端继续使用HTTP/2。

HTTP版本可以直接检索:

#{version := Version} = Req.

或使用函数:

Version = cowboy_req:version(Req).

Cowboy定义了“HTTP/1.0”、“HTTP/1.1”和“HTTP/2”版本。自定义协议可以将它们自己的值定义为原子。

有效的请求URI

有效请求URI的方案、主机、端口、路径和查询字符串组件均可直接检索:

#{
    scheme := Scheme,
    host := Host,
    port := Port,
    path := Path,
    qs := Qs
} = Req.

或使用相关功能:

Scheme = cowboy_req:scheme(Req),
Host = cowboy_req:host(Req),
Port = cowboy_req:port(Req),
Path = cowboy_req:path(Req).
Qs = cowboy_req:qs(Req).

scheme和host是不区分小写的二进制字符串。端口是一个代表端口号的整数。路径和查询字符串都是区分大小写的二进制字符串。

Cowboy只定义了<<"http">>和<<"https">>方案。选择它们,对于安全HTTP/1.1或HTTP/2连接上的请求,方案将只为<<"https">>。

有效的请求URI本身可以重构cowboy_req:uri/1,2函数。默认情况下,返回一个绝对URI:

%% scheme://host[:port]/path[?qs]
URI = cowboy_req:uri(Req).

可以禁用或替换部分或全部组件。各种URIs或URI格式可以通过这种方式生成,包括原始形式:

%% /path[?qs]
URI = cowboy_req:uri(Req, #{host => undefined}).

协议相关形式:

%% //host[:port]/path[?qs]
URI = cowboy_req:uri(Req, #{scheme => undefined}).

不带查询字符串的绝对URI:

URI = cowboy_req:uri(Req, #{qs => undefined}).

一个不同的主机:

URI = cowboy_req:uri(Req, #{host => <<"example.org">>}).

以及其他的组合。

绑定

绑定是您在定义应用程序的路由时选择提取的主机和路径组件。它们只有在路由后才可用。

Cowboy提供了检索一个或所有绑定的函数。

检索单个值:

Value = cowboy_req:binding(userid, Req).

当试图检索未绑定的值时,将返回undefined。可以提供不同的默认值:

Value = cowboy_req:binding(userid, Req, 42).

检索所有被绑定的内容:

Bindings = cowboy_req:bindings(Req).

它们作为映射返回,键是原子。

Cowboy路由器也允许你捕捉许多主机或路径段,使用…限定符。

检索从主机名捕获的段:

HostInfo = cowboy_req:host_info(Req).

路径部分:

PathInfo = cowboy_req:path_info(Req).

如果…没有在路由上使用,Cowboy将返回undefined。

查询参数

Cowboy提供了两个函数来访问查询参数。您可以使用第一个参数来获取整个参数列表。

QsVals = cowboy_req:parse_qs(Req),
{_, Lang} = lists:keyfind(<<"lang">>, 1, QsVals).

Cowboy只解析查询字符串,不做任何转换。因此,这个函数可能返回重复的参数名,或者没有关联值的参数名。返回的列表的顺序没有定义。

当查询字符串为key=1&key=2时,返回的列表将包含名为key的两个参数。

在尝试使用php风格的后缀[]时也是如此。当查询字符串为key[]=1&key[]=2时,返回的列表将包含名称key[]的两个参数。

当查询字符串只是key时,Cowboy将返回列表[{<<"key">>, true}],使用true表示参数key已定义,但没有值。

Cowboy提供的第二个函数允许您只匹配感兴趣的参数,同时使用约束执行所需的任何后处理。这个函数返回一个map。

#{id := ID, lang := Lang} = cowboy_req:match_qs([id, lang], Req).

约束可以自动应用。当id参数不是整数或lang参数为空时,以下代码片段将崩溃。同时,id的值将被转换为整数项:

QsMap = cowboy_req:match_qs([{id, int}, {lang, nonempty}], Req).

也可以提供默认值。如果没有找到lang键,将使用默认值。如果找到了键但键值为空,则不使用该键。

#{lang := Lang} = cowboy_req:match_qs([{lang, [], <<"en-US">>}], Req).

如果没有提供默认值,且缺少该值,则该查询字符串将被视为无效,进程将崩溃。

当查询字符串为key=1&key=2时,key的值为列表[1,2]。参数名称不需要包含php风格的后缀。可以使用约束来确保只传递了一个值。

报头

报头值可以作为二进制字符串检索,也可以解析为更有意义的表示形式。

获取原始值:

HeaderVal = cowboy_req:header(<<"content-type">>, Req).

Cowboy希望所有标题名都以小写二进制字符串的形式提供。无论底层协议是什么,请求和响应都是如此。

当请求中缺少标头时,将返回undefined。可以提供一个不同的默认值:

HeaderVal = cowboy_req:header(<<"content-type">>, Req, <<"text/plain">>).

所有的报头都可以被一次直接获取:

#{headers := AllHeaders} = Req.

或使用函数:

AllHeaders = cowboy_req:headers(Req).

Cowboy提供了等效的函数来解析各个报头。没有函数可以一次解析所有的报头。

解析一个特定的报头

ParsedVal = cowboy_req:parse_header(<<"content-type">>, Req).

如果不知道如何解析给定的报头,或者该值无效,则会抛出异常。已知的报头列表和默认值可以在手册中找到。

当报头丢失时,将返回undefined。您可以修改默认值。注意,它应该是直接解析的值:

ParsedVal = cowboy_req:parse_header(<<"content-type">>, Req,
    {<<"text">>, <<"plain">>, []}).

Peer(对等)

对等网络,即对等计算机网络,是一种在对等者(Peer)之间分配任务和工作负载的分布式应用架构,是对等计算模型在应用层形成的一种组网或网络形式。参考:https://baike.baidu.com/item/%E5%AF%B9%E7%AD%89%E7%BD%91%E7%BB%9C/5482934?fr=aladdin

可以直接或使用函数检索连接的对等地址和端口号。

直接检索对等者:

#{peer := {IP, Port}} = Req.

以及使用函数:

{IP, Port} = cowboy_req:peer(Req).

请注意,对等点对应于连接到服务器的远程端,这可能是客户端本身,也可能不是。它也可以是代理或网关。

 类似资料: