Web 开发人员需要能够描述其服务响应的 URI 的形状和布局。 Windows Communication Foundation (WCF) 添加了两个新类,使开发人员能够控制其 URI。 UriTemplate 和 UriTemplateTable 构成 WCF 中基于 URI 的调度引擎的基础。 这些类也可以单独使用,使开发人员能够利用模板和 URI 映射机制,而无需实现 WCF 服务。
模板
模板是描述一组相对 URI 的方法。 下表中的 URI 模板集显示了如何定义检索各种类型的天气信息的系统。
| 数据 | 模板 |
|---|---|
| 国家预测 | 天气/国家 |
| 状态预测 | weather/{state} |
| 城市预测 | weather/{state}/{city} |
| 活动预测 | weather/{state}/{city}/{activity} |
下表描述了一组结构相似的 URI。 每个条目都是 URI 模板。 大括号中的段描述变量。 不在大括号内的段落描述的是文本字符串。 WCF 模板类允许开发人员获取传入 URI,例如“/weather/wa/seattle/cycling”,并将其与描述它的模板“/weather/{state}/{city}/{activity}”匹配。
UriTemplate
UriTemplate 是封装 URI 模板的类。 构造函数采用定义模板的字符串参数。 此字符串包含下一节中所述格式的模板。 该 UriTemplate 类提供的方法允许将传入 URI 与模板匹配、从模板生成 URI、检索模板中使用的变量名称集合、确定两个模板是否等效,以及返回模板的字符串。
Match(Uri, Uri) 获取基址和候选 URI,并尝试将 URI 与模板匹配。 如果匹配成功,则返回实例 UriTemplateMatch 。 该 UriTemplateMatch 对象包含基 URI、候选 URI、查询参数的名称/值集合、相对路径段的数组、匹配的变量的名称/值集合、 UriTemplate 用于执行匹配的实例、包含候选 URI 的任何不匹配部分的字符串(在模板具有通配符时使用) 和与模板关联的对象。
注释
类 UriTemplate 在将候选 URI 与模板匹配时忽略方案和端口号。
有两种方法允许你从模板生成 URI, BindByName(Uri, NameValueCollection) 以及 BindByPosition(Uri, String[])。 BindByName(Uri, NameValueCollection) 接受基址和参数的名称/值集合。 绑定模板时,这些参数将替换为变量。 BindByPosition(Uri, String[]) 获取名称/值对,并将其从左到右替换。
ToString() 返回模板字符串。
该 PathSegmentVariableNames 属性包含模板字符串中路径段中使用的变量名称的集合。
IsEquivalentTo(UriTemplate) UriTemplate采用参数并返回一个布尔值,该值指定两个模板是否等效。 有关详细信息,请参阅本主题后面的模板等价部分。
UriTemplate 旨在处理符合 HTTP URI 语法的任何 URI 方案。 下面是支持的 URI 方案的示例。
http://https://net.tcp://net.pipe://sb://
与 URI 模板一起使用时,file:// 和 urn:// 等方案不符合 HTTP URI 语法并导致不可预知的结果。
模板字符串语法
模板有三个部分:路径、可选查询和可选片段。 有关示例,请参阅以下模板:
"/weather/{state}/{city}?forecast={length)#frag1
路径由“/weather/{state}/{city}”组成,查询由“?forecast={length}”组成,片段由“#frag1”组成。
路径表达式中开头和结尾的斜杠都是可选的。 可以完全省略查询和片段表达式。 路径由一系列由“/”分隔的段组成,每个段可以有一个文本值、一个变量名称(用 {curly braces} 写入)或通配符(以“*”形式写入)。 在上一模板中,“\weather\ 段是文本值,而”{state}“和”{city}“是变量。 变量从大括号的内容中取其名称,稍后可以使用具体值替换它们以创建 关闭的 URI。 通配符是可选的,但只能在 URI 的末尾显示,其中逻辑上与“路径的其余部分”匹配。
查询表达式(如果存在)指定由“&”分隔的一系列无序名称/值对。 查询表达式的元素可以是文本对(x=2)或变量对(x={var})。 只有查询的右侧可以有变量表达式。 不允许 {someName} = {someValue}。 不允许使用不成对的值 (?x)。 空查询表达式与只包含单个“?”的查询表达式之间没有区别(两者均表示“任何查询”)。
片段表达式可以包含文本值,不允许任何变量。
模板字符串中的所有模板变量名称必须是唯一的。 模板变量名称不区分大小写。
有效模板字符串的示例:
""
“/shoe”
“/shoe/*”
“{shoe}/boat”
“{鞋}/{船}/床/{被子}”
“shoe/{boat}”
“shoe/{boat}/*”
“shoe/boat?x=2”
“shoe/{boat}?x={bed}”
“shoe/{boat}?x={bed}&y=band”
“?x={shoe}”
“shoe?x=3&y={var}
无效模板字符串的示例:
“{shoe}/{SHOE}/x=2” - 重复变量名。
“{shoe}/boat/?bed={shoe}” - 重复变量名。
“?x=2&x=3” - 查询字符串中的名称/值对必须是唯一的,即使它们是文本也是如此。
“?x=2&” - 查询字符串格式不正确。
“?2&x={shoe}” - 查询字符串必须是名称/值对。
“?y=2&&X=3” - 查询字符串必须是名称值对,名称不能以“&”开头。
复合路径段
复合路径段允许单个 URI 路径段包含多个变量,以及组合有文本的变量。 下面是有效复合路径段的示例。
/文件名。{ext}/
/{filename}.jpg/
/{filename}。{ext}/
/{a}。{b}someLiteral{c}({d})/
下面是无效路径段的示例。
/{} - 变量必须命名。
/{shoe}{boat} - 必须用文本分隔变量。
匹配和复合路径段
复合路径段允许定义在单个路径段内具有多个变量的 UriTemplate。 例如,在以下模板字符串中:“Addresses/{state}。{city}“ 两个变量(州和城市)在同一段内定义。 此模板将匹配一个 URL,例如 http://example.com/Washington.Redmond ,但它也会与类似 URL 匹配 http://example.com/Washington.Redmond.Microsoft。 在后一种情况下,州变量将包含“Washington”,城市变量将包含“Redmond.Microsoft”。 在这种情况下,任何文本(“/”除外)都将与 {city} 变量匹配。 如果希望模板与“额外”文本不匹配,请将变量放置在单独的模板段中,例如:“Addresses/{state}/{city}”。
命名通配符段
命名通配符段是变量名称以通配符“*”开头的任何路径变量段。 下面的模板字符串包含一个名为“shoe”的命名通配符段。
"literal/{*shoe}"
通配符段必须遵循以下规则:
每个模板字符串最多可以有一个命名通配符段。
命名通配符段必须位于路径中的最右段。
命名通配符段不能与同一模板字符串中的匿名通配符段共存。
命名通配符段的名称必须是唯一的。
命名通配符段不能有默认值。
命名通配符段不能以“/”结尾。
默认变量值
使用默认变量值可为模板中的变量指定默认值。 可以通过声明变量的大括号指定默认变量,或者通过传递给 UriTemplate 构造函数的集合来指定默认变量。 以下模板显示了使用默认值指定 UriTemplate 变量的两种方法。
UriTemplate t = new UriTemplate("/test/{a=1}/{b=5}");
此模板声明了一个名为a的变量,其默认值为1,以及一个名为b的变量,其默认值为5。
注释
仅允许路径段变量具有默认值。 不允许查询字符串变量、复合段变量和命名通配符变量具有默认值。
以下代码演示如何在匹配候选 URI 时处理默认变量值。
Uri baseAddress = new Uri("http://localhost:8000/");
UriTemplate t = new UriTemplate("/{state=WA}/{city=Redmond}/", true);
Uri candidate = new Uri("http://localhost:8000/OR");
UriTemplateMatch m1 = t.Match(baseAddress, candidate);
Console.WriteLine($"Template: {t}");
Console.WriteLine($"Candidate URI: {candidate}");
// Display contents of BoundVariables
Console.WriteLine("BoundVariables:");
foreach (string key in m1.BoundVariables.AllKeys)
{
Console.WriteLine($"\t{key}={m1.BoundVariables[key]}");
}
// The output of the above code is
// Template: /{state=WA}/{city=Redmond}/
// Candidate URI: http://localhost:8000/OR
// BoundVariables:
// STATE=OR
// CITY=Redmond
注释
URI(例如 http://localhost:8000///)不与上述代码中列出的模板匹配;然而,URI(例如 http://localhost:8000/)则匹配。
以下代码演示如何使用模板创建 URI 时处理默认变量值。
Uri baseAddress = new Uri("http://localhost:8000/");
Dictionary<string,string> defVals = new Dictionary<string,string> {{"a","1"}, {"b", "5"}};
UriTemplate t = new UriTemplate("/test/{a}/{b}", defVals);
NameValueCollection vals = new NameValueCollection();
vals.Add("a", "10");
Uri boundUri = t.BindByName(baseAddress, vals);
Console.WriteLine("BaseAddress: {0}", baseAddress);
Console.WriteLine("Template: {0}", t.ToString());
Console.WriteLine("Values: ");
foreach (string key in vals.AllKeys)
{
Console.WriteLine("\tKey = {0}, Value = {1}", key, vals[key]);
}
Console.WriteLine("Bound URI: {0}", boundUri);
// The output of the preceding code is
// BaseAddress: http://localhost:8000/
// Template: /test/{a}/{b}
// Values:
// Key = a, Value = 10
// Bound URI: http://localhost:8000/test/10/5
为变量提供默认值 null 时,还有其他一些约束。 如果变量包含在模板字符串最右侧的段内,并且段右侧的所有段都具有默认值null,那么变量可以具有默认值null。 有效模板字符串的默认值如下 null:
UriTemplate t = new UriTemplate("shoe/{boat=null}");UriTemplate t = new UriTemplate("{shoe=null}/{boat=null}");UriTemplate t = new UriTemplate("{shoe=1}/{boat=null}");
以下是默认值为 null 的无效模板字符串:
UriTemplate t = new UriTemplate("{shoe=null}/boat"); // null default must be in the right most path segmentUriTemplate t = new UriTemplate("{shoe=null}/{boat=x}/{bed=null}"); // shoe cannot have a null default because boat does not have a default null value
默认值和匹配
将候选 URI 与具有默认值的模板匹配时,如果未在候选 URI 中指定值,则默认值将放在 BoundVariables 集合中。
模板等效性
当所有模板的文本匹配并且它们在同一段中具有变量时,这两个模板在结构上是 等效 的。 例如,以下模板在结构上等效:
/a/{var1}/b/{var2}?x=1&y=2
a/{x}/b%20b/{var1}?y=2&x=1
a/{y}/B%20B/{z}/?y=2&x=1
需要注意的一些事项:
如果模板包含前导斜杠,则只忽略第一个。
比较模板字符串以确定结构等效性时,变量名和路径段不区分大小写,查询字符串区分大小写。
查询字符串未排序。
UriTemplateTable
UriTemplateTable 类表示一个与开发人员选择的对象绑定的由 UriTemplate 对象组成的关联表。 在调用UriTemplateTable之前,A UriTemplate 必须至少包含一个MakeReadOnly(Boolean)。 在UriTemplateTable被调用之前,可以更改MakeReadOnly(Boolean)的内容。 在调用 MakeReadOnly(Boolean) 时会进行验证。 执行的验证类型取决于allowMultiple参数对于MakeReadOnly(Boolean)的值。
当调用MakeReadOnly(Boolean)并传入false时,UriTemplateTable会检查以确保表中没有模板。 如果找到任何结构等效的模板,则会引发异常。 当您想确保只有一个模板与传入的 URI 匹配时,请将MatchSingle(Uri)与该方法结合使用。
调用MakeReadOnly(Boolean)并传入true时,UriTemplateTable允许将多个结构上等效的模板包含在一个UriTemplateTable中。
如果添加到UriTemplate中的UriTemplateTable对象集合包含查询字符串,则这些字符串不应具有歧义。 允许相同的查询字符串。
注释
UriTemplateTable虽然允许使用 HTTP 以外的方案的基址,但将候选 URI 与模板匹配时,将忽略方案和端口号。
查询字符串多义性
如果存在与多个模板匹配的 URI,则共享等效路径的模板包含不明确的查询字符串。
以下这些查询字符串集本身是清晰明确的:
?x=1
?x=2
?x=3
?x=1&y={var}
?x=2&z={var}
?x=3
?x=1
?
? x={var}
?
?m=get&c=rss
?m=put&c=rss
?m=get&c=atom
?m=put&c=atom
以下查询字符串模板集本身不明确:
?x=1
?x={var}
“x=1” - 匹配这两个模板。
?x=1
?y=2
“x=1&y=2”匹配这两个模板。 这是因为查询字符串中包含的变量可能比其匹配的模板更多。
?x=1
?x=1&y={var}
“x=1&y=3”匹配这两个模板。
?x=3&y=4
?x=3&z=5
注释
作为 URI 路径或 UriTemplate 路径段文本的组成部分时,字符 á 和 Á 视为不同的字符(但字符 a 和 A 视为相同的字符)。 当字符 á 和 Á 显示为 {variableName} 或查询字符串的一 UriTemplate 部分时,它们被视为相同的字符(以及 a 和 A 也被视为相同的字符)。