查看源代码 uri_string (stdlib v6.2)
URI 处理函数。
此模块包含用于解析和处理 URI(RFC 3986)和表单 URL 编码查询字符串(HTML 5.2)的函数。
还支持解析和序列化非 UTF-8 表单 URL 编码查询字符串(HTML 5.0)。
URI 是一个标识符,由与 RFC 3986 中名为 URI 的语法规则匹配的字符序列组成。
通用 URI 语法由分层的组件序列组成,这些组件被称为 scheme、authority、path、query 和 fragment。
URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
hier-part = "//" authority path-abempty
/ path-absolute
/ path-rootless
/ path-empty
scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
authority = [ userinfo "@" ] host [ ":" port ]
userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
reserved = gen-delims / sub-delims
gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
/ "*" / "+" / "," / ";" / "="
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
URI 的解释仅取决于使用的字符,而不取决于这些字符在网络协议中的表示方式。
此模块实现的函数涵盖以下用例:
- 将 URI 解析为其组件并返回一个映射:
parse/1
- 将 URI 组件的映射重新组合成 URI 字符串:
recompose/1
- 更改 URI 的入站二进制和百分比编码:
transcode/2
- 将 URI 转换为规范化形式:
normalize/1
、normalize/2
- 从键值对列表组合表单 URL 编码查询字符串:
compose_query/1
、compose_query/2
- 将表单 URL 编码查询字符串分解为键值对列表:
dissect_query/1
- 解码 URI 映射或 URI 的特定组件中的百分比编码三元组:
percent_decode/1
- 准备和检索 URI 组件中包含的应用程序特定数据:
quote/1
、quote/2
、unquote/1
在处理 URI 期间存在四种不同的编码:
- 二进制中的入站二进制编码
- 列表和二进制中的入站百分比编码
- 二进制中的出站二进制编码
- 列表和二进制中的出站百分比编码
带有 uri_string/0
参数的函数接受列表、二进制和混合列表(带有二进制元素的列表)作为输入类型。除了 transcode/2
之外的所有函数都期望输入为 Unicode 码位列表、UTF-8 编码的二进制数据和 UTF-8 百分比编码的 URI 部分(“%C3%B6”对应于 Unicode 字符“ö”)。
除非另有说明,否则返回值类型和编码与输入类型和编码相同。也就是说,二进制输入返回二进制输出,列表输入返回列表输出,但混合输入返回列表输出。
对于列表,只有百分比编码。然而,在二进制文件中,应同时考虑二进制编码和百分比编码。transcode/2
提供了在支持的编码之间进行转换的方法,它接受一个 uri_string/0
和一个选项列表,指定入站和出站编码。
RFC 3986 没有强制规定任何特定的字符编码,它通常由协议或周围的文本定义。此库采用相同的假设,二进制和百分比编码作为一个配置单元处理,它们不能设置为不同的值。
引用函数旨在由 URI 生成应用程序在组件准备或检索阶段使用,以避免数据和 URI 语法中使用的字符之间发生冲突。引用函数使用百分比编码,但与例如执行 recompose/1
期间的规则不同。用户有责任仅向引用函数提供应用程序数据,并使用其输出来组合 URI 组件。
例如,引用函数可以用于构建一个路径组件,该组件包含一个包含“/”字符的段,该字符不应与路径组件中用作通用分隔符的“/”冲突。
摘要
类型
指示错误类型的错误元组。第二个组件的可能值
保存 URI 主要组件的映射。
Unicode 码位列表、UTF-8 编码的二进制数据或两者的混合,表示符合 RFC 3986 的 URI(百分比编码形式)。URI 是来自非常有限的字符集的字符序列:基本拉丁字母的字母、数字和一些特殊字符。
函数
这是一个实用函数,旨在在 shell 中使用,用于打印每个主要 URI 组件以及最重要的字符集中允许的字符。
基于 QueryList
(非百分比编码的键值对列表)组合一个表单 URL 编码的 QueryString
。
与 compose_query/1
相同,但带有一个额外的 Options
参数,用于控制编码算法使用的编码(“字符集”)。
分解一个 URL 编码的 QueryString
并返回一个 QueryList
(非百分比编码的键值对列表)。
使用 RFC 3986 定义的基于语法的规范化将 URI
转换为规范化形式。
与 normalize/1
相同,但带有一个额外的 Options
参数,用于控制是否将规范化的 URI 作为 uri_map() 返回。
将符合 RFC 3986 的 uri_string/0
解析为一个 uri_map/0
,该映射保存 URI
的已解析组件。如果解析失败,则返回错误元组。
解码输入中所有百分比编码的三元组,输入可以是 uri_string/0
和 uri_map/0
。
将非保留集之外的字符替换为其百分比编码的等效字符。
与 quote/1
相同,但 Safe
允许用户提供要保护不被编码的字符列表。
基于 URIMap
的组件创建符合 RFC 3986 的 URIString
(百分比编码)。如果 URIMap
无效,则返回错误元组。
将可能相对于给定基本 URI 的 RefURI
引用转换为引用目标的已解析组件,然后可以将其重新组合以形成目标 URI。
与 resolve/2
相同,但带有一个额外的 Options
参数,用于控制是否将目标 URI 作为 uri_map() 返回。有一个支持的选项:return_map
。
转码符合 RFC 3986 的 URIString
,其中 Options
是一个标记元组列表,指定入站 (in_encoding
) 和出站 (out_encoding
) 编码。
百分比解码字符。
类型
指示错误类型的错误元组。第二个组件的可能值
invalid_character
invalid_encoding
invalid_input
invalid_map
invalid_percent_encoding
invalid_scheme
invalid_uri
invalid_utf8
missing_value
第三个组件是一个术语,提供有关错误原因的其他信息。
-type uri_map() :: #{fragment => unicode:chardata(), host => unicode:chardata(), path => unicode:chardata(), port => non_neg_integer() | undefined, query => unicode:chardata(), scheme => unicode:chardata(), userinfo => unicode:chardata()}.
保存 URI 主要组件的映射。
-type uri_string() :: iodata().
Unicode 码位列表、UTF-8 编码的二进制数据或两者的混合,表示符合 RFC 3986 的 URI(百分比编码形式)。URI 是来自非常有限的字符集的字符序列:基本拉丁字母的字母、数字和一些特殊字符。
函数
这是一个实用函数,旨在在 shell 中使用,用于打印每个主要 URI 组件以及最重要的字符集中允许的字符。
请注意,此函数不会替换标准定义的 ABNF 规则,这些字符集直接从上述规则派生而来。有关详细信息,请参阅 stdlib 用户指南中的统一资源标识符章节。
-spec compose_query(QueryList) -> QueryString when QueryList :: [{unicode:chardata(), unicode:chardata() | true}], QueryString :: uri_string() | error().
基于 QueryList
(非百分比编码的键值对列表)组合一个表单 URL 编码的 QueryString
。
表单 URL 编码在 HTML 5.2 规范的 4.10.21.6 节和 HTML 5.0 规范的 4.10.22.6 节中定义,用于非 UTF-8 编码。
另请参阅相反的操作 dissect_query/1
。
示例
1> uri_string:compose_query([{"foo bar","1"},{"city","örebro"}]).
"foo+bar=1&city=%C3%B6rebro"
2> uri_string:compose_query([{<<"foo bar">>,<<"1">>},
2> {<<"city">>,<<"örebro"/utf8>>}]).
<<"foo+bar=1&city=%C3%B6rebro">>
-spec compose_query(QueryList, Options) -> QueryString when QueryList :: [{unicode:chardata(), unicode:chardata() | true}], Options :: [{encoding, atom()}], QueryString :: uri_string() | error().
与 compose_query/1
相同,但带有一个额外的 Options
参数,用于控制编码算法使用的编码(“字符集”)。
支持两种编码:utf8
(或 unicode
) 和 latin1
。
条目名称和值中,任何无法使用所选字符编码表示的字符,都会被替换为一个字符串,该字符串由一个 U+0026 AMPERSAND 字符 (&),一个 "#" (U+0023) 字符,一个或多个表示该字符 Unicode 代码点(十进制)的 ASCII 数字,以及最后的一个 ";" (U+003B) 字符组成。
超出 0x2A、0x2D、0x2E、0x30 到 0x39、0x41 到 0x5A、0x5F、0x61 到 0x7A 范围的字节会进行百分比编码(使用 U+0025 PERCENT SIGN 字符 (%),后跟表示该字节十六进制值的大写 ASCII 十六进制数字)。
另请参阅相反的操作 dissect_query/1
。
示例
1> uri_string:compose_query([{"foo bar","1"},{"city","örebro"}],
1> [{encoding, latin1}]).
"foo+bar=1&city=%F6rebro"
2> uri_string:compose_query([{<<"foo bar">>,<<"1">>},
2> {<<"city">>,<<"東京"/utf8>>}], [{encoding, latin1}]).
<<"foo+bar=1&city=%26%2326481%3B%26%2320140%3B">>
-spec dissect_query(QueryString) -> QueryList when QueryString :: uri_string(), QueryList :: [{unicode:chardata(), unicode:chardata() | true}] | error().
分解一个 URL 编码的 QueryString
并返回一个 QueryList
(非百分比编码的键值对列表)。
表单 URL 编码在 HTML 5.2 规范的 4.10.21.6 节和 HTML 5.0 规范的 4.10.22.6 节中定义,用于非 UTF-8 编码。
另请参阅相反的操作 compose_query/1
。
示例
1> uri_string:dissect_query("foo+bar=1&city=%C3%B6rebro").
[{"foo bar","1"},{"city","örebro"}]
2> uri_string:dissect_query(<<"foo+bar=1&city=%26%2326481%3B%26%2320140%3B">>).
[{<<"foo bar">>,<<"1">>},
{<<"city">>,<<230,157,177,228,186,172>>}]
-spec normalize(URI) -> NormalizedURI when URI :: uri_string() | uri_map(), NormalizedURI :: uri_string() | error().
使用 RFC 3986 定义的基于语法的规范化将 URI
转换为规范化形式。
此函数实现了 HTTP(S) 的大小写规范化、百分比编码规范化、路径段规范化和基于方案的规范化,并基本支持 FTP、SSH、SFTP 和 TFTP。
示例
1> uri_string:normalize("/a/b/c/./../../g").
"/a/g"
2> uri_string:normalize(<<"mid/content=5/../6">>).
<<"mid/6">>
3> uri_string:normalize("https://127.0.0.1:80").
"https://127.0.0.1/"
4> uri_string:normalize(#{scheme => "http",port => 80,path => "/a/b/c/./../../g",
4> host => "localhost-örebro"}).
"https://127.0.0.1-%C3%B6rebro/a/g"
-spec normalize(URI, Options) -> NormalizedURI when URI :: uri_string() | uri_map(), Options :: [return_map], NormalizedURI :: uri_string() | uri_map() | error().
与 normalize/1
相同,但带有一个额外的 Options
参数,用于控制是否将规范化的 URI 作为 uri_map() 返回。
支持一个选项:return_map
。
示例
1> uri_string:normalize("/a/b/c/./../../g", [return_map]).
#{path => "/a/g"}
2> uri_string:normalize(<<"mid/content=5/../6">>, [return_map]).
#{path => <<"mid/6">>}
3> uri_string:normalize("https://127.0.0.1:80", [return_map]).
#{scheme => "http",path => "/",host => "localhost"}
4> uri_string:normalize(#{scheme => "http",port => 80,path => "/a/b/c/./../../g",
4> host => "localhost-örebro"}, [return_map]).
#{scheme => "http",path => "/a/g",host => "localhost-örebro"}
-spec parse(URIString) -> URIMap when URIString :: uri_string(), URIMap :: uri_map() | error().
将符合 RFC 3986 的 uri_string/0
解析为一个 uri_map/0
,该映射保存 URI
的已解析组件。如果解析失败,则返回错误元组。
另请参阅相反的操作 recompose/1
。
示例
1> uri_string:parse("foo://[email protected]:8042/over/there?name=ferret#nose").
#{fragment => "nose",host => "example.com",
path => "/over/there",port => 8042,query => "name=ferret",
scheme => foo,userinfo => "user"}
2> uri_string:parse(<<"foo://[email protected]:8042/over/there?name=ferret">>).
#{host => <<"example.com">>,path => <<"/over/there">>,
port => 8042,query => <<"name=ferret">>,scheme => <<"foo">>,
userinfo => <<"user">>}
-spec percent_decode(URI) -> Result when URI :: uri_string() | uri_map(), Result :: uri_string() | uri_map() | {error, {invalid, {atom(), {term(), term()}}}} | error().
解码输入中所有百分比编码的三元组,输入可以是 uri_string/0
和 uri_map/0
。
请注意,此函数执行原始解码,应在已解析的 URI 组件上使用。直接在标准 URI 上应用此函数可能会有效地更改它。
如果输入编码不是 UTF-8,则会返回错误元组。
示例
1> uri_string:percent_decode(#{host => "localhost-%C3%B6rebro",path => [],
1> scheme => "http"}).
#{host => "localhost-örebro",path => [],scheme => "http"}
2> uri_string:percent_decode(<<"%C3%B6rebro">>).
<<"örebro"/utf8>>
警告
直接在 URI 上使用
uri_string:percent_decode/1
是不安全的。此示例显示,在每次连续应用该函数后,生成的 URI 将被更改。这些 URI 都不是指同一个资源。3> uri_string:percent_decode(<<"http://local%252Fhost/path">>). <<"http://local%2Fhost/path">> 4> uri_string:percent_decode(<<"http://local%2Fhost/path">>). <<"http://local/host/path">>
-spec quote(Data) -> QuotedData when Data :: unicode:chardata(), QuotedData :: unicode:chardata().
将非保留集之外的字符替换为其百分比编码的等效字符。
RFC 3986 中定义的未保留字符不会被引用。
示例
1> uri_string:quote("SomeId/04").
"SomeId%2F04"
2> uri_string:quote(<<"SomeId/04">>).
<<"SomeId%2F04">>
警告
该函数不知道任何 URI 组件上下文,不应在整个 URI 上使用。如果对同一数据应用多次,可能会产生意外的结果。
-spec quote(Data, Safe) -> QuotedData when Data :: unicode:chardata(), Safe :: string(), QuotedData :: unicode:chardata().
与 quote/1
相同,但 Safe
允许用户提供要保护不被编码的字符列表。
示例
1> uri_string:quote("SomeId/04", "/").
"SomeId/04"
2> uri_string:quote(<<"SomeId/04">>, "/").
<<"SomeId/04">>
警告
该函数不知道任何 URI 组件上下文,不应在整个 URI 上使用。如果对同一数据应用多次,可能会产生意外的结果。
-spec recompose(URIMap) -> URIString when URIMap :: uri_map(), URIString :: uri_string() | error().
基于 URIMap
的组件创建符合 RFC 3986 的 URIString
(百分比编码)。如果 URIMap
无效,则返回错误元组。
另请参阅相反的操作 parse/1
。
示例
1> URIMap = #{fragment => "nose", host => "example.com", path => "/over/there",
1> port => 8042, query => "name=ferret", scheme => "foo", userinfo => "user"}.
#{fragment => "nose",host => "example.com",
path => "/over/there",port => 8042,query => "name=ferret",
scheme => "foo",userinfo => "user"}
2> uri_string:recompose(URIMap).
"foo://example.com:8042/over/there?name=ferret#nose"
-spec resolve(RefURI, BaseURI) -> TargetURI when RefURI :: uri_string() | uri_map(), BaseURI :: uri_string() | uri_map(), TargetURI :: uri_string() | error().
将可能相对于给定基本 URI 的 RefURI
引用转换为引用目标的已解析组件,然后可以将其重新组合以形成目标 URI。
示例
1> uri_string:resolve("/abs/ol/ute", "https://127.0.0.1/a/b/c?q").
"https://127.0.0.1/abs/ol/ute"
2> uri_string:resolve("../relative", "https://127.0.0.1/a/b/c?q").
"https://127.0.0.1/a/relative"
3> uri_string:resolve("https://127.0.0.1/full", "https://127.0.0.1/a/b/c?q").
"https://127.0.0.1/full"
4> uri_string:resolve(#{path => "path", query => "xyz"}, "https://127.0.0.1/a/b/c?q").
"https://127.0.0.1/a/b/path?xyz"
-spec resolve(RefURI, BaseURI, Options) -> TargetURI when RefURI :: uri_string() | uri_map(), BaseURI :: uri_string() | uri_map(), Options :: [return_map], TargetURI :: uri_string() | uri_map() | error().
与 resolve/2
相同,但带有一个额外的 Options
参数,用于控制是否将目标 URI 作为 uri_map() 返回。有一个支持的选项:return_map
。
示例
1> uri_string:resolve("/abs/ol/ute", "https://127.0.0.1/a/b/c?q", [return_map]).
#{host => "localhost",path => "/abs/ol/ute",scheme => "http"}
2> uri_string:resolve(#{path => "/abs/ol/ute"}, #{scheme => "http",
2> host => "localhost", path => "/a/b/c?q"}, [return_map]).
#{host => "localhost",path => "/abs/ol/ute",scheme => "http"}
-spec transcode(URIString, Options) -> Result when URIString :: uri_string(), Options :: [{in_encoding, unicode:encoding()} | {out_encoding, unicode:encoding()}], Result :: uri_string() | error().
转码符合 RFC 3986 的 URIString
,其中 Options
是一个标记元组列表,指定入站 (in_encoding
) 和出站 (out_encoding
) 编码。
in_encoding
和 out_encoding
指定输入和输出数据的二进制编码和百分比编码。不支持混合编码,即二进制编码与百分比编码不相同。如果参数无效,则会返回错误元组。
示例
1> uri_string:transcode(<<"foo%00%00%00%F6bar"/utf32>>,
1> [{in_encoding, utf32},{out_encoding, utf8}]).
<<"foo%C3%B6bar"/utf8>>
2> uri_string:transcode("foo%F6bar", [{in_encoding, latin1},
2> {out_encoding, utf8}]).
"foo%C3%B6bar"
-spec unquote(QuotedData) -> Data when QuotedData :: unicode:chardata(), Data :: unicode:chardata().
百分比解码字符。
示例
1> uri_string:unquote("SomeId%2F04").
"SomeId/04"
2> uri_string:unquote(<<"SomeId%2F04">>).
<<"SomeId/04">>
警告
该函数不知道任何 URI 组件上下文,不应在整个 URI 上使用。如果对同一数据应用多次,可能会产生意外的结果。