Web 開発者には、サービスが応答する URI の形状とレイアウトを記述する機能が必要です。 Windows Communication Foundation (WCF) では、開発者が URI を制御できるように、2 つの新しいクラスが追加されました。 UriTemplate と UriTemplateTable WCF の URI ベースのディスパッチ エンジンの基礎を形成します。 これらのクラスは独自に使用することもできます。開発者は、WCF サービスを実装せずにテンプレートと URI マッピング メカニズムを利用できます。
テンプレート
テンプレートは、一連の相対 URI を記述する方法です。 次の表の URI テンプレートのセットは、さまざまな種類の気象情報を取得するシステムがどのように定義されるかを示しています。
データ | テンプレート |
---|---|
国内予測 | weather/national |
状態予測 | weather/{state} |
市区町村の予測 | weather/{state}/{city} |
アクティビティ予測 | weather/{state}/{city}/{activity} |
次の表では、構造的に似た URI のセットについて説明します。 各エントリは URI テンプレートです。 中かっこ内のセグメントは変数を表します。 中かっこに含まれていないセグメントは、リテラル文字列を表します。 WCF テンプレート クラスを使用すると、開発者は受信 URI ("/weather/wa/seattle/cycle" など) を取得し、それを "/weather/{state}/{city}/{activity}" を記述するテンプレートと照合できます。
UriTemplate
UriTemplate は、URI テンプレートをカプセル化するクラスです。 コンストラクターは、テンプレートを定義する文字列パラメーターを受け取ります。 この文字列には、次のセクションで説明する形式のテンプレートが含まれています。 UriTemplate クラスは、テンプレートへの受信 URI の照合、テンプレートからの URI の生成、テンプレートで使用される変数名のコレクションの取得、2 つのテンプレートが同等であるかどうかを判断し、テンプレートの文字列を返すメソッドを提供します。
Match(Uri, Uri) はベース アドレスと候補 URI を受け取り、URI とテンプレートの照合を試みます。 一致が成功すると、 UriTemplateMatch インスタンスが返されます。 UriTemplateMatch オブジェクトには、ベース URI、候補 URI、クエリ パラメーターの名前/値コレクション、相対パス セグメントの配列、一致した変数の名前/値コレクション、一致を実行するために使用されるUriTemplate インスタンス、候補 URI の一致しない部分 (テンプレートにワイルドカードがある場合に使用) を含む文字列が含まれます。 およびテンプレートに関連付けられているオブジェクト。
注
UriTemplate クラスは、候補 URI をテンプレートに照合するときに、スキームとポート番号を無視します。
テンプレートから URI を生成するには、 BindByName(Uri, NameValueCollection) と BindByPosition(Uri, String[])の 2 つの方法があります。 BindByName(Uri, NameValueCollection) は、ベース アドレスとパラメーターの名前/値コレクションを受け取ります。 テンプレートがバインドされている場合、これらのパラメーターは変数に置き換えます。 BindByPosition(Uri, String[]) は名前と値のペアを受け取り、左から右に置き換えます。
ToString() はテンプレート文字列を返します。
PathSegmentVariableNames プロパティには、テンプレート文字列内のパス セグメント内で使用される変数の名前のコレクションが含まれています。
IsEquivalentTo(UriTemplate) はパラメーターとして UriTemplate を受け取り、2 つのテンプレートが等しいかどうかを示すブール値を返します。 詳細については、このトピックで後述する「テンプレートの等価性」セクションを参照してください。
UriTemplate は、HTTP URI 文法に準拠するすべての URI スキームを操作するように設計されています。 サポートされている URI スキームの例を次に示します。
http://
https://
net.tcp://
net.pipe://
sb://
file:// や urn:// などのスキームは、HTTP URI 文法に準拠していないため、URI テンプレートで使用すると予期しない結果が発生します。
テンプレート文字列の構文
テンプレートには、パス、省略可能なクエリ、および省略可能なフラグメントの 3 つの部分があります。 例については、次のテンプレートを参照してください。
"/weather/{state}/{city}?forecast={length)#frag1
パスは "/weather/{state}/{city}" で構成され、クエリは "?forecast={length}" で構成され、フラグメントは "#frag1" で構成されます。
パス式では、先頭と末尾のスラッシュは省略可能です。 クエリ式とフラグメント式の両方を完全に省略できます。 パスは、'/' で区切られた一連のセグメントで構成され、各セグメントにはリテラル値、変数名 ({中かっこ}で記述)、またはワイルドカード ('*' として書き込まれます) を使用できます。 前のテンプレートでは、"\weather\ セグメントはリテラル値ですが、"{state}" と "{city}" は変数です。 変数は中かっこの内容から名前を取得し、後で具体的な値に置き換えて 、閉じた URI を作成できます。 ワイルドカードは省略可能ですが、URI の末尾にのみ表示できます。URI は論理的に "残りのパス" と一致します。
クエリ式が存在する場合は、順序指定されていない一連の名前と値のペアを '> で区切って指定します。 クエリ式の要素には、リテラル ペア (x=2) または変数ペア (x={var}) を指定できます。 変数式を使用できるのは、クエリの右側だけです。 ({someName} = {someValue} は許可されていません。 ペアになっていない値 (?x) は許可されません。 空のクエリ式と、単一の '?' で構成されるクエリ式に違いはありません。(どちらも "任意のクエリ" を意味します)。
フラグメント式はリテラル値で構成でき、変数は使用できません。
テンプレート文字列内のすべてのテンプレート変数名は一意である必要があります。 テンプレート変数名では大文字と小文字が区別されません。
有効なテンプレート文字列の例:
""
"/shoe"
"/shoe/*"
"{shoe}/boat"
"{shoe}/{boat}/bed/{キルト}"
"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" – クエリ文字列は名前と値のペアである必要があります。名前は '> で始めることはできません。
複合パス セグメント
複合パス セグメントを使用すると、1 つの URI パス セグメントに複数の変数と、リテラルと組み合わせた変数を含めることができます。 有効な複合パス セグメントの例を次に示します。
/filename。{ext}/
/{filename}.jpg/
/{filename}。{ext}/
/{a}。{b}someLiteral{c}({d})/
無効なパス セグメントの例を次に示します。
/{} - 変数に名前を付ける必要があります。
/{shoe}{boat} - 変数はリテラルで区切る必要があります。
一致パス セグメントと複合パス セグメント
複合パス セグメントを使用すると、1 つのパス セグメント内に複数の変数を含む UriTemplate を定義できます。 たとえば、次のテンプレート文字列に "Addresses/{state}" と入力します。{city}" 2 つの変数 (state と city) が同じセグメント内で定義されています。 このテンプレートは、 http://example.com/Washington.Redmond
などの URL と一致しますが、 http://example.com/Washington.Redmond.Microsoft
のような URL にも一致します。 後者の場合、状態変数には "Washington" が含まれており、市区町村変数には "Redmond.Microsoft" が含まれます。 この場合、任意のテキスト ('/' を除く) が {city} 変数と一致します。 "extra" テキストと一致しないテンプレートが必要な場合は、変数を別のテンプレート セグメント ("Addresses/{state}/{city}" など) に配置します。
名前付きワイルドカード セグメント
名前付きワイルドカード セグメントは、変数名がワイルドカード文字 '*' で始まる任意のパス変数セグメントです。 次のテンプレート文字列には、"shoe" という名前のワイルドカード セグメントが含まれています。
"literal/{*shoe}"
ワイルドカード セグメントは、次の規則に従う必要があります。
テンプレート文字列ごとに、最大 1 つの名前付きワイルドカード セグメントを使用できます。
名前付きワイルドカード セグメントは、パス内の右端のセグメントに表示する必要があります。
名前付きワイルドカード セグメントは、同じテンプレート文字列内の匿名ワイルドカード セグメントと共存できません。
名前付きワイルドカード セグメントの名前は一意である必要があります。
名前付きワイルドカード セグメントに既定値を設定することはできません。
名前付きワイルドカード セグメントを "/" で終えることはできません。
既定の変数値
既定の変数値を使用すると、テンプレート内の変数の既定値を指定できます。 既定の変数は、変数を宣言する中かっこで指定することも、UriTemplate コンストラクターに渡されるコレクションとして指定することもできます。 次のテンプレートは、既定値を持つ変数を持つ UriTemplate を指定する 2 つの方法を示しています。
UriTemplate t = new UriTemplate("/test/{a=1}/{b=5}");
このテンプレートは、既定値が 1
の a
という名前の変数と、既定値が 5
の b
という名前の変数を宣言します。
注
パス セグメント変数のみが既定値を持つことができます。 クエリ文字列変数、複合セグメント変数、および名前付きワイルドカード変数は、既定値を使用できません。
次のコードは、候補 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
注
http://localhost:8000///
などの URI は、上記のコードに記載されているテンプレートと一致しませんが、http://localhost:8000/
などの URI は一致します。
次のコードは、テンプレートを使用して 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 コレクションに配置されます。
テンプレートの等価性
2 つのテンプレートは、すべてのテンプレートのリテラルが一致し、同じセグメントに変数がある場合に 構造的に同等 であると言われます。 たとえば、次のテンプレートは構造的に同等です。
/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
注目すべき点がいくつかあります。
テンプレートに先頭のスラッシュが含まれている場合、最初のスラッシュのみが無視されます。
テンプレート文字列を構造の等価性と比較する場合、変数名とパス セグメントでは大文字と小文字が区別され、クエリ文字列では大文字と小文字が区別されます。
クエリ文字列は順序付けされません。
UriTemplateTable
UriTemplateTable クラスは、開発者が選択したオブジェクトにバインドUriTemplateオブジェクトの連想テーブルを表します。
MakeReadOnly(Boolean)を呼び出す前に、UriTemplateTableに少なくとも 1 つのUriTemplateを含める必要があります。
UriTemplateTableの内容は、MakeReadOnly(Boolean)が呼び出されるまで変更できます。 検証は、 MakeReadOnly(Boolean) が呼び出されたときに実行されます。 実行される検証の種類は、MakeReadOnly(Boolean)するallowMultiple
パラメーターの値によって異なります。
false
を渡すMakeReadOnly(Boolean)が呼び出されると、UriTemplateTableはテーブルにテンプレートがないことを確認します。 構造的に同等のテンプレートが見つかると、例外がスローされます。 これは、1 つのテンプレートのみが受信 URI と一致するようにする場合に、 MatchSingle(Uri) と組み合わせて使用されます。
MakeReadOnly(Boolean)がtrue
渡しと呼ばれる場合、UriTemplateTableでは、構造上同等の複数のテンプレートをUriTemplateTable内に含めることができます。
UriTemplateTableに追加された一連のUriTemplate オブジェクトにクエリ文字列が含まれている場合は、あいまいにすることはできません。 同じクエリ文字列を使用できます。
注
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 も同じ文字と見なされます) の一部として出現する場合、同じ文字と見なされます。