Поделиться через


UriTemplate и UriTemplateTable

Для веб-разработчиков требуется возможность описать форму и макет URI, на которые отвечают их службы. Windows Communication Foundation (WCF) добавил два новых класса, чтобы предоставить разработчикам контроль над своими URI. UriTemplate и UriTemplateTable формируют основу движка диспетчеризации на основе URI в WCF. Эти классы также можно использовать самостоятельно, позволяя разработчикам воспользоваться шаблонами и механизмом сопоставления URI без реализации службы WCF.

Шаблоны

Шаблон — это способ описания набора относительных URI. Набор шаблонов URI в следующей таблице показывает, как система, которая извлекает различные типы информации о погоде, может быть определена.

Данные Шаблон
Национальный прогноз погода/национальная
Прогноз состояния погода/{state}
Прогноз города погода/{state}/{city}
Прогноз действий погода/{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 кандидата и пытается сопоставить его с шаблоном. Если совпадение выполнено успешно, возвращается экземпляр 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 предназначен для работы с любой схемой URI, которая соответствует грамматике URI HTTP. Ниже приведены примеры поддерживаемых схем URI.

  • http://

  • https://

  • net.tcp://

  • net.pipe://

  • sb://

Схемы, такие как file:// и urn://, не соответствуют грамматике URI HTTP и приводят к непредсказуемым результатам при использовании с шаблонами URI.

Синтаксис строки шаблона

Шаблон содержит три части: путь, необязательный запрос и необязательный фрагмент. Пример см. в следующем шаблоне:

"/weather/{state}/{city}?forecast={length)#frag1

Путь состоит из "/weather/{state}/{city}", запрос состоит из "?forecast={length}, а фрагмент состоит из "#frag1".

В выражении пути начальные и конечные косые черты являются необязательными. Выражения запроса и фрагмента могут быть полностью опущены. Путь состоит из ряда сегментов, разделенных "/", каждый сегмент может иметь литеральное значение, имя переменной (написанное в {фигурные скобки}) или подстановочный знак (написанный как "*"). В предыдущем шаблоне сегмент "\weather\ является литеральным значением, а "{state}" и "{city}" являются переменными. Переменные берут своё имя из содержимого фигурных скобок, и затем они могут быть заменены конкретным значением для создания закрытого URI. Подстановочный знак является необязательным, но может отображаться только в конце URI, где он логически соответствует "остальной части пути".

Выражение запроса, если оно присутствует, указывает ряд неупорядоченных пар «имя-значение», разделенных символом «&». Элементы выражения запроса могут быть литеральными парами (x=2) или переменной (x={var}). Только правая часть запроса может иметь выражение переменной. ({someName} = {someValue} не допускается. Непарные значения (?x) не допускаются. Нет разницы между пустым выражением запроса и выражением запроса, состоящим только из одного "?" (оба означают "любой запрос").

Выражение фрагмента может состоять из литерального значения, переменные не допускаются.

Все имена переменных шаблона в строке шаблона должны быть уникальными. Имена переменных шаблона не учитывают регистр.

Примеры допустимых строк шаблона:

  • ""

  • "/обувь"

  • "/обувь/*"

  • "{обувь}/лодка"

  • {shoe}/{boat}/кровать/{quilt}

  • "обувь/{лодка}"

  • "обувь/{лодка}/*"

  • "обувь/лодка?x=2"

  • обувь/{лодка}?x={кровать}

  • обувь/{лодка}?x={кровать}&y=band

  • ?x={обувь}

  • "обувь?x=3&y={var}

Примеры недопустимых строк шаблона:

  • "{shoe}/{SHOE}/x=2" — повторяющиеся имена переменных.

  • "{shoe}/boat/?bed={shoe}" — повторяющиеся имена переменных.

  • "?x=2&x=3" — пары name/value в строке запроса должны быть уникальными, даже если они являются литералами.

  • "?x=2&" — строка запроса неправильно сформирована.

  • "?2&x={shoe}" — строка запроса должна содержать пары имя-значение.

  • "?y=2&&X=3" — строка запроса должна быть парами значений имен, имена не могут начинаться с '&'.

Сегменты составных путей

Сегменты составных путей позволяют одному сегменту пути URI содержать несколько переменных, а также переменные в сочетании с литералами. Ниже приведены примеры допустимых сегментов составных путей.

  • /имя файла. {ext}/

  • /{filename}.jpg/

  • /{filename}. {ext}/

  • /{a}. {b}someLiteral{c}({d})/

Ниже приведены примеры сегментов недопустимых путей.

  • /{} - Переменные должны быть названы.

  • /{shoe}{boat} — переменные должны быть разделены специальным символом.

Сопоставление и составные сегменты пути

Сегменты составных путей позволяют определить UriTemplate с несколькими переменными в одном сегменте пути. Например, в следующей строке шаблона: "Адреса/{state}. {city}" две переменные (штат и город) определяются в одном сегменте. Этот шаблон будет соответствовать URL-адресу, например http://example.com/Washington.Redmond , но он также будет соответствовать URL-адресу, например http://example.com/Washington.Redmond.Microsoft. В последнем случае переменная штата будет содержать "Вашингтон", а городская переменная будет содержать "Redmond.Microsoft". В этом случае любой текст (за исключением "/" будет соответствовать переменной {city}. Если требуется шаблон, который не будет соответствовать тексту "экстра", поместите переменную в отдельный сегмент шаблона, например "Адреса/{state}/{city}.

Именованные сегменты подстановочных знаков

Именованный семантический сегмент — это любой сегмент пути переменной, имя переменной которого начинается с подстановочного знака "*". Следующая строка шаблона содержит именованный сегмент подстановочного знака с именем "обувь".

"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 кандидата с шаблоном, который имеет значения по умолчанию, значения по умолчанию помещаются в BoundVariables коллекцию, если значения не указаны в URI кандидата.

Эквивалентность шаблона

Два шаблона считаются структурно эквивалентными, если все литералы шаблонов совпадают и они имеют переменные в одинаковых сегментах. Например, следующие шаблоны являются структурно эквивалентны.

  • /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 содержать по крайней мере один UriTemplate перед вызовом MakeReadOnly(Boolean). Содержимое UriTemplateTable можно изменить до вызова MakeReadOnly(Boolean). Проверка выполняется, когда вызывается MakeReadOnly(Boolean). Тип выполняемой проверки зависит от значения allowMultiple параметра MakeReadOnly(Boolean).

При вызове MakeReadOnly(Boolean) с передачей false, UriTemplateTable проверяет, чтобы в таблице не было шаблонов. Если он находит какие-либо структурны эквивалентные шаблоны, он создает исключение. Это используется в сочетании с MatchSingle(Uri) в тех случаях, когда требуется убедиться, что только один шаблон соответствует входящему 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 считаются одинаковыми). Символы á и Á считаются одинаковыми, когда они отображаются как часть UriTemplate {variableName} или строки запроса (и a и A также считаются одинаковыми символами).

См. также