UriTemplate 和 UriTemplateTable

Web 开发人员需要能够描述其服务响应的 URI 的形状和布局。 Windows Communication Foundation (WCF) 添加了两个新类,使开发人员能够控制其 URI。 UriTemplateUriTemplateTable 构成 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 segment

  • UriTemplate 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 也被视为相同的字符)。

另请参阅