UriTemplate and UriTemplateTable
Os desenvolvedores Web exigem a capacidade de descrever a forma e o layout das URIs às quais seus serviços respondem. O WCF (Windows Communication Foundation) adicionou duas novas classes para dar aos desenvolvedores controle sobre suas URIs. UriTemplate e UriTemplateTable formam a base do mecanismo de expedição baseado em URI no WCF. Essas classes também podem ser usadas sozinhas, permitindo que os desenvolvedores aproveitem os modelos e o mecanismo de mapeamento de URI sem implementar um serviço WCF.
Modelos
Um modelo é uma maneira de descrever um conjunto de URIs relativas. O conjunto de modelos de URI na tabela a seguir mostra como pode ser definido um sistema que recupera vários tipos de informações meteorológicas.
Dados | Modelo |
---|---|
Previsão nacional | clima/nacional |
Previsão estadual | clima/{estado} |
Previsão da cidade | clima/{estado}/{cidade} |
Previsão de atividade | clima/{estado}/{cidade}/{atividade} |
Esta tabela descreve um conjunto de URIs estruturalmente semelhantes. Cada entrada é um modelo de URI. Os segmentos em chaves descrevem variáveis. Os segmentos que não estão em chaves descrevem cadeias de caracteres literais. As classes de modelo do WCF permitem que um desenvolvedor pegue um URI de entrada, por exemplo, "/clima/wa/seattle/ciclismo" e corresponda a um modelo que o descreva, "/clima/{estado}/{cidade}/{atividade}".
UriTemplate
UriTemplate é uma classe que encapsula um modelo de URI. O construtor usa um parâmetro de cadeia de caracteres que define o modelo. Essa cadeia de caracteres contém o modelo no formato descrito na próxima seção. A classe UriTemplate fornece métodos que permitem corresponder um URI de entrada a um modelo, gerar um URI de um modelo, recuperar uma coleção de nomes de variáveis usados no modelo, determinar se dois modelos são equivalentes e retornar a cadeia de caracteres do modelo.
Match(Uri, Uri) usa um endereço básico e um URI candidato e tenta corresponder o URI ao modelo. Se a correspondência tiver êxito, uma instância UriTemplateMatch será retornada. O objeto UriTemplateMatch contém um URI base, o URI candidato, uma coleção de nome/valor dos parâmetros de consulta, uma matriz dos segmentos de linha relativa, uma coleção de nome/valor de variáveis que foram correspondentes, a instância UriTemplate usada para executar a correspondência, uma cadeia de caracteres que contém qualquer parte incompatível do URI candidato (usado quando o modelo tem um curinga), e um objeto associado ao modelo.
Observação
A classe UriTemplate ignora o esquema e o número da porta ao corresponder um URI candidato a um modelo.
Dois métodos permitem gerar um URI de um modelo: BindByName(Uri, NameValueCollection) e BindByPosition(Uri, String[]). BindByName(Uri, NameValueCollection) usa um endereço básico e uma coleção de nome/valor de parâmetros. Esses parâmetros são substituídos por variáveis quando o modelo é associado. BindByPosition(Uri, String[]) usa os pares nome/valor e os substitui da esquerda para a direita.
ToString() retorna a cadeia de caracteres do modelo.
A propriedade PathSegmentVariableNames contém uma coleção dos nomes das variáveis usadas nos segmentos de linha na cadeia de caracteres de modelo.
IsEquivalentTo(UriTemplate) usa um UriTemplate como parâmetro e retorna um valor booliano que especifica se os dois modelos são equivalentes. Para mais informações, consulte a seção Equivalência de modelos mais adiante neste tópico.
UriTemplate foi projetado para funcionar com qualquer esquema de URI que esteja em conformidade com a gramática de URI HTTP. Veja a seguir exemplos de esquemas de URI suportados.
http://
https://
net.tcp://
net.pipe://
sb://
Esquemas como file:// e urn:// não estão em conformidade com a gramática de URI HTTP e causam resultados imprevisíveis quando usados com modelos de URI.
Sintaxe da cadeia de caracteres de modelo
Um modelo tem três partes: um caminho, uma consulta opcional e um fragmento opcional. Por exemplo, veja o modelo a seguir:
"/weather/{state}/{city}?forecast={length)#frag1
O caminho consiste em "/clima/{estado}/{cidade}", a consulta consiste em "?forecast={length}, e o fragmento consiste em "#frag1".
As barras à esquerda e à direita são opcionais na expressão de caminho. As expressões de consulta e fragmento podem ser totalmente omitidas. Um caminho consiste em uma série de segmentos delimitados por '/'. Cada segmento pode ter um valor literal, um nome de variável (escrito em {chaves}) ou um curinga (escrito como '*'). No modelo anterior, o segmento "\clima\ é um valor literal, enquanto "{estado}" e "{cidade}" são variáveis. As variáveis levam seu nome a partir do conteúdo de suas chaves e podem ser substituídas posteriormente por um valor concreto para criar um URI fechado. O curinga é opcional, mas só pode aparecer no final do URI, em que corresponde logicamente ao "restante do caminho".
A expressão de consulta, se presente, especifica uma série de pares de nome/valor não ordenados delimitados por “&”. Os elementos da expressão de consulta podem ser pares literais (x=2) ou um par de variáveis (x={var}). Somente o lado direito da consulta pode ter uma expressão variável. ({someName} = {someValue} não é permitido. Valores não pareados (?x) não são permitidos. Não há diferença entre uma expressão de consulta vazia e uma expressão de consulta que consiste em apenas um único “?” (ambos significam “qualquer consulta”).
A expressão de fragmento pode consistir em um valor literal, variáveis não são permitidas.
Todos os nomes de variáveis de modelo em uma cadeia de caracteres de modelo devem ser únicos. Os nomes de variáveis de modelo não diferenciam maiúsculas de minúsculas.
Exemplos de cadeias de caracteres de modelo válidas:
""
"/shoe"
"/shoe/*"
"{shoe}/boat"
"{shoe}/{boat}/bed/{quilt}"
"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}
Exemplos de cadeias de caracteres de modelo inválidas:
"{shoe}/{SHOE}/x=2" – Nomes de variáveis duplicados.
"{shoe}/boat/?bed={shoe}" – Nomes de variáveis duplicados.
"?x=2&x=3" – Os pares nome/valor dentro de uma cadeia de caracteres de consulta precisam ser exclusivos, mesmo que sejam literais.
"?x=2&" – A cadeia de caracteres de consulta está malformada.
"?2&x={shoe}" – A cadeia de caracteres de consulta deve ser pares nome/valor.
"?y=2&X=3" – A cadeia de caracteres de consulta deve ser pares de nome/valor, os nomes não podem começar com “&”.
Segmentos de linha composta
Os segmentos de linha composta permitem que um único segmento de linha de URI contenha várias variáveis, bem como variáveis combinadas com literais. Veja a seguir exemplos de segmentos de linha composta válidos.
/filename.{ext}/
/{filename}.jpg/
/{filename}.{ext}/
/{a}.{b}someLiteral{c}({d})/
Veja a seguir exemplos de segmentos de linha inválidos.
/{} – As variáveis devem ser nomeadas.
/{shoe}{boat} – As variáveis devem ser separadas por um literal.
Segmentos de linha composta e correspondente
Os segmentos de linha composta permitem que você defina um UriTemplate que tenha várias variáveis em um único segmento de linha. Por exemplo, na seguinte cadeia de caracteres de modelo: "Endereços/{estado}.{cidades}", duas variáveis (estado e cidade) são definidas no mesmo segmento. Esse modelo corresponderia a uma URL como http://example.com/Washington.Redmond
, mas também corresponderá a uma URL como http://example.com/Washington.Redmond.Microsoft
. No último caso, a variável de estado conterá "Washington" e a variável da cidade conterá "Redmond.Microsoft". Nesse caso, qualquer texto (exceto “/”) corresponderá à variável {cidade}. Se você quiser um modelo que não corresponda ao texto "extra", coloque a variável em um segmento de modelo separado, por exemplo: "Endereços/{estado}/{cidade}.
Segmentos curinga nomeados
Um segmento curinga nomeado é qualquer segmento de variável de linha cujo nome de variável começa com o caractere curinga “*”. A cadeia de caracteres de modelo a seguir contém um segmento curinga nomeado chamado "shoe".
"literal/{*shoe}"
Os segmentos curinga devem seguir as seguintes regras:
Pode haver no máximo um segmento curinga nomeado para cada cadeia de caracteres de modelo.
Um segmento curinga nomeado deve aparecer no segmento mais à direita na linha.
Um segmento curinga nomeado não pode coexistir com um segmento curinga anônimo dentro da mesma cadeia de caracteres de modelo.
O nome de um segmento curinga nomeado deve ser único.
Segmentos curinga nomeados não podem ter valores padrão.
Segmentos curinga nomeados não podem terminar com "/".
Valores de variável de objeto
Os valores de variáveis padrão permitem que você especifique valores padrão para variáveis em um modelo. Variáveis padrão podem ser especificadas com as chaves que declaram a variável ou como uma coleção passada para o construtor UriTemplate. O modelo a seguir mostra duas maneiras de especificar um UriTemplate com variáveis com valores padrão.
UriTemplate t = new UriTemplate("/test/{a=1}/{b=5}");
Este modelo declara uma variável nomeada a
com um valor padrão de 1
e uma variável nomeada b
com um valor padrão de 5
.
Observação
Somente variáveis de segmento de linha têm permissão para ter valores padrão. Variáveis de cadeia de caracteres de consulta, variáveis de segmento composto e variáveis curinga nomeadas não têm permissão para ter valores padrão.
O código a seguir mostra como os valores de variáveis padrão são tratados ao corresponder a um URI candidato.
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
Observação
Um URI como http://localhost:8000///
não corresponde ao modelo listado no código anterior, no entanto, um URI como http://localhost:8000/
, sim.
O código a seguir mostra como os valores de variáveis padrão são tratados ao criar um URI com um modelo.
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
Quando uma variável recebe um valor padrão de null
, há algumas restrições adicionais. Uma variável pode ter um valor padrão de null
se a variável estiver contida no segmento mais correto da cadeia de caracteres de modelo ou se todos os segmentos à direita do segmento tiverem valores padrão de null
. Veja a seguir cadeias de caracteres de modelo válidas com valores padrão de null
:
UriTemplate t = new UriTemplate("shoe/{boat=null}");
UriTemplate t = new UriTemplate("{shoe=null}/{boat=null}");
UriTemplate t = new UriTemplate("{shoe=1}/{boat=null}");
Veja a seguir cadeias de caracteres de modelo inválidas com valores padrão de 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
Valores padrão e correspondência
Ao combinar um URI candidato com um modelo com valores padrão, os valores padrão serão colocados na coleção BoundVariables se os valores não forem especificados no URI candidato.
Equivalência do modelo
Dizem que dois modelos são estruturalmente equivalentes quando todos os literais dos modelos correspondem e têm variáveis nos mesmos segmentos. Por exemplo, os modelos a seguir são estruturalmente equivalentes:
/a/{var1}/b b/{var2}?x=1&y=2
a/{x}/b%20b/{var1}?y=2&x=1
a/{y}/B%20B/{z}/?y=2&x=1
Algumas observações:
Se um modelo contiver barras principais, somente a primeira será ignorada.
Ao comparar cadeias de caracteres de modelo para equivalência estrutural, o caso é ignorado para nomes de variáveis e segmentos de linha, as cadeias de caracteres de consulta diferenciam maiúsculas de minúsculas.
As cadeias de caracteres de consulta não são ordenadas.
UriTemplateTable
A classe UriTemplateTable representa uma tabela associativa de UriTemplate objetos associados a um objeto de escolha do desenvolvedor. Um UriTemplateTable deve conter pelo menos um UriTemplate antes de chamar MakeReadOnly(Boolean). O conteúdo de um UriTemplateTable pode ser alterado até MakeReadOnly(Boolean) ser chamado. A validação é executada quando MakeReadOnly(Boolean) é chamado. O tipo de validação executado depende do valor do parâmetro allowMultiple
para MakeReadOnly(Boolean).
Quando MakeReadOnly(Boolean) é chamado de passagem em false
, UriTemplateTable verifica se não há modelos na tabela. Se encontrar modelos estruturalmente equivalentes, uma exceção será gerada. Isso é usado em conjunto com MatchSingle(Uri) quando você deseja garantir que apenas um modelo corresponda a um URI de entrada.
Quando MakeReadOnly(Boolean) é chamado de passagem em true
, UriTemplateTable permite que vários modelos estruturalmente equivalentes sejam contidos em um UriTemplateTable.
Se um conjunto de UriTemplate objetos adicionados a um UriTemplateTable contiver cadeias de caracteres de consulta, elas não devem ser ambíguas. Cadeias de caracteres de consulta idênticas são permitidas.
Observação
Embora UriTemplateTable permita endereços básicos que usam esquemas diferentes de HTTP, o esquema e o número da porta são ignorados ao combinar URIs candidatos a modelos.
Ambiguidade da cadeia de caracteres de consulta
Os modelos que compartilham um caminho equivalente contêm cadeias de caracteres de consulta ambíguas se houver um URI que corresponda a mais de um modelo.
Os seguintes conjuntos de cadeias de caracteres de consulta não são ambíguas entre si:
?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
Os seguintes conjuntos de cadeias de caracteres de consulta são ambíguas entre si:
?x=1
?x={var}
"x=1" – Corresponde aos dois modelos.
?x=1
?y=2
"x=1&y=2" corresponde aos dois modelos. Isso ocorre porque uma cadeia de caracteres de consulta pode conter mais variáveis de cadeia de caracteres de consulta do que o modelo correspondente.
?x=1
?x=1&y={var}
"x=1&y=3" corresponde aos dois modelos.
?x=3&y=4
?x=3&z=5
Observação
Os caracteres á e Á são considerados caracteres diferentes quando aparecem como parte de um caminho de URI ou UriTemplate literal do segmento de linha (mas os caracteres a e A são considerados iguais). Os caracteres á e Á são considerados os mesmos quando aparecem como parte de um UriTemplate {Nomevariável} ou uma cadeia de caracteres de consulta (e a e A também são considerados os mesmos).