UriTemplate и UriTemplateTable

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

Шаблоны

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

Data Template
Прогноз для страны weather/national
Прогноз для штата 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 (используется, если шаблон содержит подстановочный знак) и объект, связанный с этим шаблоном.

Примечание.

При сопоставлении потенциального универсального кода ресурса (URI) класс UriTemplate не учитывает схему и номер порта.

Есть два метода, позволяющие создавать 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».

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

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

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

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

Ниже приведены допустимые строки шаблона:

  • ""

  • "/shoe"

  • "/обувь/*"

  • "{shoe}/boat"

  • "{shoe}/{boat}/bed/{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}некоторый_литерал{c}({d})/

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

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

  • /{shoe}{boat} - переменные должны разделяться литералом.

Совпадающие и составные сегменты пути

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

См. также