LoginSignup
2
2

More than 5 years have passed since last update.

ASP.NET Core で可変長なURLを作るCatch-Allパラメータの紹介

Posted at

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 パラメータの紹介でした。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2