UriTemplate e UriTemplateTable
Os desenvolvedores da Web exigem a capacidade de descrever a forma e o layout dos URIs aos quais seus serviços respondem. O Windows Communication Foundation (WCF) adicionou duas novas classes para dar aos desenvolvedores controle sobre seus URIs. UriTemplate e UriTemplateTable formam a base do mecanismo de despacho baseado em URI no WCF. Essas classes também podem ser usadas por conta própria, 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 relativos. O conjunto de modelos de URI na tabela a seguir mostra como um sistema que recupera vários tipos de informações meteorológicas pode ser definido.
Dados | Template |
---|---|
Previsão Nacional | Tempo/Nacional |
Previsão do Estado | tempo/{estado} |
Previsão da cidade | tempo/{estado}/{cidade} |
Previsão de Atividades | tempo/{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 entre chaves descrevem cadeias literais. As classes de modelo WCF permitem que um desenvolvedor pegue um URI de entrada, por exemplo, "/weather/wa/seattle/cycling", e o combine com um modelo que o descreva, "/weather/{state}/{city}/{activity}".
UriTemplate
UriTemplate é uma classe que encapsula um modelo de URI. O construtor usa um parâmetro string que define o modelo. Esta cadeia de caracteres contém o modelo no formato descrito na próxima seção. A UriTemplate classe fornece métodos que permitem que você corresponda um URI de entrada a um modelo, gere um URI de um modelo, recupere uma coleção de nomes de variáveis usados no modelo, determine se dois modelos são equivalentes e retorne a cadeia de caracteres do modelo.
Match(Uri, Uri) usa um endereço base e um URI candidato e tenta corresponder o URI ao modelo. Se a correspondência for bem-sucedida, uma UriTemplateMatch instância será retornada. O UriTemplateMatch objeto contém um URI base, o URI candidato, uma coleção de nome/valor dos parâmetros de consulta, uma matriz dos segmentos de caminho relativos, uma coleção de nome/valor de variáveis que foram correspondidas, a UriTemplate instância usada para executar a correspondência, uma cadeia de caracteres que contém qualquer parte incomparável do URI candidato (usado quando o modelo tem um curinga), e um objeto associado ao modelo.
Nota
A UriTemplate classe ignora o esquema e o número da porta ao fazer a correspondência de um URI candidato a um modelo.
Há dois métodos que permitem gerar um URI a partir de um modelo BindByName(Uri, NameValueCollection) e BindByPosition(Uri, String[]). BindByName(Uri, NameValueCollection) usa um endereço base e uma coleção de nome/valor de parâmetros. Esses parâmetros são substituídos por variáveis quando o modelo é vinculado. BindByPosition(Uri, String[]) pega os pares nome/valor e os substitui da esquerda para a direita.
ToString() Retorna a cadeia de caracteres do modelo.
A PathSegmentVariableNames propriedade contém uma coleção dos nomes das variáveis usadas nos segmentos de caminho na cadeia de caracteres do modelo.
IsEquivalentTo(UriTemplate) toma um UriTemplate como parâmetro e retorna um valor booleano que especifica se os dois modelos são equivalentes. Para obter mais informações, consulte a seção Equivalência de modelo 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. Seguem-se 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 do modelo
Um modelo tem três partes: um caminho, uma consulta opcional e um fragmento opcional. Para obter um exemplo, consulte o seguinte modelo:
"/weather/{state}/{city}?forecast={length)#frag1
O caminho consiste em "/weather/{state}/{city}", 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 curvas}) ou um curinga (escrito como '*'). No modelo anterior, o segmento "\weather\ é um valor literal, enquanto "{state}" e "{city}" são variáveis. As variáveis tomam 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, onde corresponde logicamente "ao resto do caminho".
A expressão de consulta, se presente, especifica uma série de pares 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}). Apenas o lado direito da consulta pode ter uma expressão variável. ({someName} = {someValue} não é permitido. Valores não emparelhados (?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 do fragmento pode consistir em um valor literal, nenhuma variável é permitida.
Todos os nomes de variáveis de modelo dentro de uma cadeia de caracteres de modelo devem ser exclusivos. Os nomes das variáveis de modelo não diferenciam maiúsculas de minúsculas.
Exemplos de cadeias de caracteres de modelo válidas:
""
"/sapato"
"/sapato/*"
"{sapato}/barco"
"{shoe}/{boat}/bed/{quilt}"
"Sapato/{barco}"
"Sapato/{barco}/*"
"Sapato/barco?x=2"
"sapato/{barco}?x={cama}"
"sapato/{barco}?x={cama}&y=banda"
"?x={sapato}"
"Sapato?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 devem 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 valor de nome, os nomes não podem começar com '&'.
Segmentos de caminho compostos
Os segmentos de caminho compostos permitem que um único segmento de caminho URI contenha várias variáveis, bem como variáveis combinadas com literais. A seguir estão exemplos de segmentos de caminho composto válidos.
/nome do arquivo. {ext}/
/{nome do arquivo}.jpg/
/{nome do arquivo}. {ext}/
/{a}. {b}someLiteral{c}({d})/
A seguir estão exemplos de segmentos de caminho inválidos.
/{} - As variáveis devem ser nomeadas.
/{shoe}{boat} - As variáveis devem ser separadas por um literal.
Correspondência e segmentos de caminho composto
Os segmentos de caminho compostos permitem definir um UriTemplate que tem várias variáveis dentro de um único segmento de caminho. Por exemplo, na seguinte cadeia de caracteres de modelo: "Addresses/{state}. {cidade}" duas variáveis (estado e cidade) são definidas dentro do mesmo segmento. Este modelo corresponderia a um URL como http://example.com/Washington.Redmond
, mas também corresponderia a um URL como http://example.com/Washington.Redmond.Microsoft
. Neste último caso, a variável state conterá "Washington" e a variável city conterá "Redmond.Microsoft". Neste caso, qualquer texto (exceto '/') corresponderá à variável {city}. 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 caminho cujo nome da variável começa com o caractere curinga '*'. A seqüência 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 no caminho.
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 exclusivo.
Os segmentos curinga nomeados não podem ter valores padrão.
Os segmentos curinga nomeados não podem terminar com "/".
Valores de variáveis padrão
Os valores de variáveis padrão permitem especificar valores padrão para variáveis dentro de um modelo. As 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
.
Nota
Somente variáveis de segmento de caminho podem ter valores padrão. Variáveis de cadeia de caracteres de consulta, variáveis de segmento composto e variáveis curinga nomeadas não podem ter valores padrão.
O código a seguir mostra como os valores de variáveis padrão são manipulados 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
Nota
Um URI como http://localhost:8000///
não corresponde ao modelo listado no código anterior, no entanto, um URI como http://localhost:8000/
o faz.
O código a seguir mostra como os valores de variáveis padrão são manipulados 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
existem algumas restrições adicionais. Uma variável pode ter um valor padrão de null
se a variável estiver contida no segmento mais à direita da cadeia de caracteres do modelo ou se todos os segmentos à direita do segmento tiverem valores padrão de null
. A seguir estão 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}");
A seguir estão 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 fazer a correspondência de um URI candidato com um modelo que tenha valores padrão, os valores padrão serão colocados na coleção se os BoundVariables valores não forem especificados no URI candidato.
Equivalência de modelo
Dois modelos são considerados estruturalmente equivalentes quando todos os literais dos modelos correspondem e eles têm variáveis nos mesmos segmentos. Por exemplo, os seguintes modelos 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 coisas a notar:
Se um modelo contiver barras à esquerda, apenas a primeira será ignorada.
Ao comparar cadeias de caracteres de modelo para equivalência estrutural, maiúsculas e minúsculas são ignoradas para nomes de variáveis e segmentos de caminho, as cadeias de caracteres de consulta diferenciam maiúsculas de minúsculas.
As cadeias de caracteres de consulta não estão ordenadas.
UriTemplateTable
A UriTemplateTable classe representa uma tabela associativa de UriTemplate objetos vinculados a um objeto escolhido pelo desenvolvedor. A UriTemplateTable deve conter pelo menos um UriTemplate antes de chamar MakeReadOnly(Boolean). O conteúdo de um UriTemplateTable pode ser alterado até MakeReadOnly(Boolean) que seja chamado. A validação é realizada quando MakeReadOnly(Boolean) é chamada. O tipo de validação realizada depende do valor do allowMultiple
parâmetro para MakeReadOnly(Boolean).
Quando MakeReadOnly(Boolean) é chamado de passagem false
, as UriTemplateTable verificações para se certificar de que não há modelos na tabela. Se encontrar modelos estruturalmente equivalentes, lança uma exceção. 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 passing in true
, UriTemplateTable permite que vários modelos estruturalmente equivalentes sejam contidos em um UriTemplateTablearquivo .
Se um conjunto de objetos adicionados a uma UriTemplateTable cadeia de caracteres de consulta contiver cadeias de UriTemplate caracteres de consulta, eles não devem ser ambíguos. Cadeias de caracteres de consulta idênticas são permitidas.
Nota
Enquanto o UriTemplateTable permite endereços base que usam esquemas diferentes de HTTP, o esquema e o número da porta são ignorados ao fazer a correspondência de 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 são inequívocos dentro de si mesmos:
?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 modelos de cadeia de caracteres de consulta são ambíguos em si mesmos:
?x=1
?x={var}
"x=1" - Corresponde a ambos os modelos.
?x=1
?y=2
"x=1&y=2" corresponde a ambos os 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 ao qual ela corresponde.
?x=1
?x=1&y={var}
"x=1&y=3" corresponde a ambos os modelos.
?x=3&y=4
?x=3&z=5
Nota
Os caracteres á e Á são considerados caracteres diferentes quando aparecem como parte de um caminho URI ou UriTemplate segmento de caminho literal (mas os caracteres a e A são considerados os mesmos). Os caracteres á e Á são considerados os mesmos caracteres quando aparecem como parte de um UriTemplate {variableName} ou uma cadeia de caracteres de consulta (e a e A também são considerados os mesmos caracteres).