ASP.NET Core で Web アプリを作る時、
http://**.com/api/a/b/c/..../hoge
(※a,b,c, ... の数は可変)
みたいなURLを作る方法をまとめます。
結論
可変にしたいパラメータの頭に *
を付ける!以上!
[HttpGet("sample/{*segments}")]
参考:https://docs.microsoft.com/ja-jp/aspnet/core/fundamentals/routing?view=aspnetcore-2.2
使いどころ
例えば GitLab の場合、リポジトリ中のファイルを表示するページは以下のURLになります。
http(s)://{サーバ名}/{グループ名}/{プロジェクト名}/tree/{ブランチ名}
ところが、このグループは階層構造を取れるため、
- groupA
- groupA/groupB
- groupA/groupB/groupC
という形式を取れます。
これをURLにすると
- http(s)://{サーバ名}/groupA/{プロジェクト名}/tree/{ブランチ名}
- http(s)://{サーバ名}/groupA/groupB/{プロジェクト名}/tree/{ブランチ名}
- http(s)://{サーバ名}/groupA/groupB/groupC/{プロジェクト名}/tree/{ブランチ名}
となり、URLが可変長になってしまいます。
こんなURLをルーティングするWebサービスを作りたい場合には、Catch-Allパラメータが有用です。
実装例
ValuesController.cs
[HttpGet("sample/{*segments}")]
public ActionResult<string> GetVariableSegments([FromRoute] string segments)
{
return segments;
}
PS Z:\> Invoke-RestMethod -Method GET -Uri http://localhost:1377/api/values/sample/a/b/c
a/b/c
*
をつけ忘れると、404 Not Found です。
ValuesController.cs
[HttpGet("sample/{segments}")] //*つけ忘れ
public ActionResult<string> GetVariableSegments([FromRoute] string segments)
{
return segments;
}
PS Z:\> Invoke-RestMethod -Method GET -Uri http://localhost:1377/api/values/sample/a/b/c
Invoke-RestMethod : リモート サーバーがエラーを返しました: (404) 見つかりません
発生場所 行:1 文字:1
+ Invoke-RestMethod -Method GET -Uri http://localhost:1377/api/values/g ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod]、WebExce
ption
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
他にマッチするURLがあれば、そっちが優先されます。
ValuesController.cs
[HttpGet("sample/pre/{*segments}")]
public ActionResult<string> GetVariableSegmentsWithPre([FromRoute] string segments)
{
return "preだよ!" + segments;
}
[HttpGet("sample/{*segments}")]
public ActionResult<string> GetVariableSegments([FromRoute] string segments)
{
return segments;
}
[HttpGet("sample/post/{*segments}")]
public ActionResult<string> GetVariableSegmentsWithPost([FromRoute] string segments)
{
return "postだよ!" + segments;
}
PS Z:\> Invoke-RestMethod -Method GET -Uri http://localhost:1377/api/values/sample/pre/a/b/c
preだよ!a/b/c
PS Z:\> Invoke-RestMethod -Method GET -Uri http://localhost:1377/api/values/sample/post/a/b/c
postだよ!a/b/c
PS Z:\> Invoke-RestMethod -Method GET -Uri http://localhost:1377/api/values/sample/none/a/b/c
none/a/b/c
条件
Catch-Allパラメータは必ず最後のセグメントに定義されなければいけません。
sample/{*segments}/postfix
みたいなのはダメです。以下の例外が出ます。(クエリパラメータは入ってもOK)
ArgumentException: A catch-all parameter can only appear as the last segment of the route template. Parameter name: routeTemplate
どうしてもセグメントを足したい場合、Actionメソッド内で頑張って条件分岐を書くしかなさそうです。
ValuesController.cs
[HttpGet("sample/{*segments}")]
public ActionResult<string> GetVariableSegments([FromRoute] string segments)
{
string[] segmentsArray = segments.Split('/');
//最後の一つで条件分岐
string postfix = segmentsArray[segmentsArray.Length - 1];
string result;
switch (postfix)
{
case "hoge":
result = "hogeだよ!";
break;
case "huga":
result = "hugaだよ!";
break;
default:
return new CustomJsonResult(HttpStatusCode.BadRequest, "error!");
}
return new CustomJsonResult(HttpStatusCode.OK, result);
}
以上、ASP.NET Core の Catch-All パラメータの紹介でした。