作成者: Ryan Nowak、Kirk Larkin、Rick Anderson
Note
これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 10 バージョンを参照してください。
Warning
このバージョンの ASP.NET Coreはサポートされなくなりました。 詳細については、.NETおよびコア サポート ポリシー.NETを参照してください。 現在のリリースについては、この記事の .NET 10 バージョンを参照してください。
ASP.NET Core コントローラーは、ルーティング middleware を使用して受信要求の URL を照合し、それらを actions にマップします。 ルート テンプレート:
- "" または属性で起動時に定義されます。
- URL パスとアクションの照合方法が記述されています。
- リンクの URL を生成するために使用されます。 生成されたリンクは通常、応答で返されます。
アクションは、規則的にルーティングされるか、または属性でルーティングされます。 コントローラーまたはアクションにルートを配置すると、そのルートは属性でルーティングされるようになります。 詳しくは、「混合ルーティング」をご覧ください。
このドキュメントは次のとおりです。
- MVC とルーティングの間の相互作用について説明します。
- 一般的な MVC アプリでルーティング機能を利用する方法。
- 次の両方について説明します。
- 従来のルーティングは通常、コントローラーとビューで使用されます。
- API で使用される。 API のルーティングに主に関心がある場合は、「 API の属性ルーティング」セクションに進んでください。
- 高度なルーティングについて詳しくは、ルーティングに関する記事を参照してください。
- エンドポイント ルーティングとして既定のルーティング システムを参照します。 互換性のために、以前のバージョンのルーティングでコントローラーを使用できます。 手順については、2.2-3.0 の移行ガイドを参照してください。
従来のルートの設定
ASP.NET Core MVC テンプレートは、次の例のような一意のルーティング コードを生成します。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
を使用して 1 つのルートを作成します。 特定のルートが唯一のルートです。 コントローラーとビューを使用するほとんどのアプリでは、 ルートと同様のルート テンプレートが使用されます。 API では、属性ルーティングを使用する必要があります。
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
ルート テンプレート :
のような URL パスと一致します
パスをトークン化して、ルートの値 を抽出します。 アプリに という名前のコントローラーと アクションがある場合、ルート値の抽出が一致します。
public class ProductsController : Controller { public IActionResult Details(int id) { return ControllerContext.MyDisplayRouteInfo(id); } }MyDisplayRouteInfo は Rick.Docs.Samples.RouteInfo NuGet パッケージによって提供され、ルート情報が表示されます。
モデルは、 の値をバインドして、 パラメーターを に設定します。 詳細については、「 モデル バインド」を参照してください。
は、 を既定の として定義します。
は、 を既定の として定義します。
の文字 は、 を省略可能として定義します。
- 既定および省略可能のルート パラメーターは、URL パスに存在していなくても一致します。 ルート テンプレートの構文の詳細については、「 ルート テンプレート リファレンス」を参照してください。
URL パス と一致します。
ルート値 を生成します。
と の値に、既定値が使用されます。 URL パスに対応するセグメントがないため、 は値を生成しません。 と アクションが存在する場合のみ、 は一致します。
public class HomeController : Controller
{
public IActionResult Index() { ... }
}
上記のコントローラー定義とルート テンプレートを使用すると、 アクションは次の URL パスに対して実行されます。
/Home/Index/17/Home/Index/Home/
URL パス では、ルート テンプレートの既定の コントローラーと アクションが使用されます。 URL パス では、ルート テンプレートの既定の アクションが使用されます。
便利なメソッド :
app.MapDefaultControllerRoute();
Replaces:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Important
ルーティングは、 と ミドルウェアを使用して構成されます。 コントローラーを使用するには、
- を呼び出して、属性でルーティングされたコントローラーをマップします。
- または を呼び出して、規則的にルーティングされたコントローラーと属性でルーティングされたコントローラーの両方をマップします。
通常は、アプリで または を呼び出す必要はありません。 は、とを使用して、に追加されたミドルウェアをラップするミドルウェア パイプラインを構成します。 詳細については、「
従来のルーティング
コントローラーとビューで従来のルーティングを使用します。 ルートは次のとおりです。
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
上記のコードは、 従来のルートの例です。 これを "規則ルーティング" と呼ぶのは、それが URL パスの "規則" を作成するためです。
- 最初のパス セグメント は、コントローラー名にマップします。
- 2 番目のセグメント は、アクション名にマップします。
- 3 番目のセグメント は、省略可能な に使われます。 の によって、省略可能になります。 それはモデルエンティティにマップされます。
この ルートを使用すると、URL パスは次のようになります。
- は、 アクションにマップします。
- は、 にマップし、通常 パラメーターを 17 にバインドします。
このマッピング:
- コントローラーとアクションの名前にのみ基づきます。
- 名前空間、ソース ファイルの場所、またはメソッドのパラメーターには基づきません。
既定のルートで従来のルーティングを使用することで、アクションごとに新しい URL パターンを作成する必要はありません。 CRUD スタイルのアクションを使用するアプリの場合、コントローラー間で URL の一貫性を保つことは、
- コードを簡略化するのに役立ちます。
- UI の予測可能性を向上させます。
Warning
ルート テンプレートは、 を省略可能として定義します。 アクションは、URL の一部として指定された省略可能な ID なしで実行できます。 通常、URL から を省略すると、次のようになります。
- モデル バインドは、 を に設定します。
- データベース照合 でエンティティが見つかりません。
属性ルーティングを使うと、ID が必須のアクションと必須ではないアクションをきめ細かく制御できます。 慣例に従って、ドキュメントには などの省略可能なパラメーターが正しい使用法で使われる可能性が高い場合に記載されています。
ほとんどのアプリでは、URL を読みやすくてわかりやすいものにするために、基本的なでわかりやすいルーティング スキームを選択する必要があります。 既定の規則ルート :
- 基本的でわかりやすいルーティング スキームをサポートしています。
- UI ベースのアプリの便利な開始点となります。
- 多くの Web UI アプリに必要な唯一のルート テンプレートになります。 大規模な Web UI アプリの場合でも、大抵は区分を使用するもう 1 つのルートがあれば十分です。
と では、
- それぞれが呼び出された順序に基づいて、それぞれのエンドポイントに順序値が自動的に割り当てられます。
ASP.NET Coreでのエンドポイント ルーティング:
- ルートの概念がありません。
- 拡張性の実行に対する順序付けの保証は提供されません。 すべてのエンドポイントが一度に処理されます。
ログを有効にすると、 など、組み込みのルーティング実装で要求を照合するしくみを確認できます。
属性ルーティングについては、このドキュメントの後で説明します。
複数の従来のルート
とへの呼び出しを追加することで、複数の従来のルートを構成できます。 これらの呼び出しを追加するときに、複数の規則を定義したり、特定の アクション専用の従来のルートを追加したりできます。
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
上の例の が、専用の規則ルートです。 これは、次の理由から、専用の従来のルートです。
- 規則ルーティングを使用します。
- 特定のアクション専用です。
ルート テンプレート には、パラメーターとして と が含まれていないためです。
- 既定値 しか指定できません。
- このルートは常にアクション にマップされます。
、、および は、ブログ ルートに一致する唯一の URL パスです。
前の例の場合:
- 最初に追加するため、このルートはそのルートよりも一致の優先度が高くなります。
- これは、URL の一部として記事名を付けるのが一般的な Slug スタイルのルーティングの例です。
Warning
ASP.NET Coreでは、ルーティングが次の処理を行いません。
- "ルート" と呼ばれる概念の定義。 はミドルウェア パイプラインにルートの照合を追加します。 ミドルウェアによって、アプリで定義されているエンドポイントのセットが調べられ、要求に基づいて最適な一致が選択されます。
- や のような機能拡張の実行順序に関する保証。
詳細については、ルーティングに関する記事を参照してください。
従来のルーティング順序
従来のルーティングは、アプリが定義するアクションとコントローラーの組み合わせにのみ一致します。 この方法により、従来のルートが重なる場合が簡略化されます。 、、およびを使用してルートを追加すると、エンドポイントは、これらのメソッドを呼び出した順序に基づいて注文値を自動的に取得します。 一覧の前に表示されたルートからの一致は、優先度が高くなります。 従来のルーティングは順序によって異なります。 一般に、エリアのないルートよりも具体的なルートであるため、前の場所にルートを配置します。 たとえば、キャッチオールルートパラメーターを持つ専用の従来のルートは、ルートが過度に適用されることがあります。 他のルートで照合することを意図した URL を貪欲ルートが一致させてしまうことがあります。 意図しないルートまで一致しないようにするため、一致範囲が広いルートを後ろに置きます。
Warning
ルーティングでバグが原因で、キャッチオール パラメーターがルートと正しく一致しない可能性があります。 このバグの影響を受けるアプリには、次の特性があります。
- キャッチオール ルート (たとえば、)
- キャッチオール ルートが、一致すべき要求と一致しません。
- 他のルートを削除すると、キャッチオール ルートが機能し始めます。
このGitHubバグに関連するケースとして、18677および16579を参照してください。
このバグのオプトイン修正は、.NET Core 3.1.301 以降の SDK に含まれています。 次のコードにより、このバグを修正する内部スイッチが設定されます。
public static void Main(string[] args)
{
AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior",
true);
CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.
あいまいなアクションの解決
ルーティング経由で 2 つのエンドポイントが一致する場合、ルーティングでは次のいずれかの手順を実行する必要があります。
- 最適な候補を選択します。
- 例外をスローします。
例えば次が挙げられます。
public class Products33Controller : Controller
{
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpPost]
public IActionResult Edit(int id, Product product)
{
return ControllerContext.MyDisplayRouteInfo(id, product.name);
}
}
上のコントローラーでは、一致する次の 2 つのアクションが定義されています。
- URL パス
- ルート データ
これは MVC コントローラーの一般的なパターンです。
- には、製品を編集するフォームが表示されます。
- は、ポストされたフォームを処理します。
正しいルートを解決するため、
- は、要求が HTTP の場合に選択されます。
- は、HTTP 動詞がそれ以外の場合に選択されます。 は通常、 を介して呼び出されます。
() は、要求の HTTP メソッドに基づいて選択できるようルーティングに提供されます。 は、 よりも の一致を向上させます。
のような属性の役割を理解することが重要です。 同様の属性は、他の HTTP 動詞に対して定義されます。 従来のルーティングでは、多くの場合、アクションが表示フォームの一部である場合に同じアクション名を使用し、フォーム ワークフローを送信します。 たとえば、2 つの編集アクションのメソッドに関する説明を参照してください。
ルーティングで最適な候補を選択できない場合は、 がスローされ、一致する複数のエンドポイントが一覧表示されます。
従来のルート名
次の例の文字列 と は、規則ルート名です。
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
ルート名は、ルートに論理名を付けます。 名前付きルートは、URL の生成に使用できます。 ルートの順序指定によって URL の生成が複雑になる場合に、名前付きルートを使用すると、URL の作成が大幅に簡略化されます。 ルート名は、アプリケーション全体で一意である必要があります。
ルート名:
- URL の照合や要求の処理に影響を与えません。
- URL の生成にのみ使用されます。
ルート名の概念は、ルーティングで IEndpointNameMetadata として表されます。 ルート名とエンドポイント名は、
- 交換可能です。
- ドキュメントとコードでどちらが使用されるかは、説明されている API によって異なります。
API の属性ルーティング
API では、属性ルーティングを使用して、HTTP 動詞で操作を表現するリソースのセットとしてアプリの機能をモデル化する必要があります。
属性ルーティングでは、属性のセットを使ってアクションをルート テンプレートに直接マップします。 次のコードは、 API では一般的であり、次のサンプルで使用されます。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
前のコードでは、「属性ルーティングコントローラーをマップする」メソッドを呼び出します。
次に例を示します。
- は、既定ルート が一致するのと同様に、一連の URL に一致します。
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult Index(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult About(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
アクションは、、、、またはのいずれかの URL パスに対して実行されます。
この例では、属性ルーティングと規則ルーティングでのプログラミングの大きな違いが強調して示されています。 属性ルーティングでは、ルートを指定するために追加の入力が必要です。 既定の規則ルートは、より簡潔にルートを処理します。 ただし、属性ルーティングでは、各アクションに適用するルート テンプレートを正確に制御できます (そして制御する必要があります)。
属性ルーティングでは、トークンの置換が使用されていない限り、コントローラーとアクションの名前はアクションの照合で果たす役割はありません。 次の例は、前の例と同じ URL と一致します。
public class MyDemoController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult MyIndex(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult MyAbout(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
次のコードでは、 と のトークン置換が使用されます。
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("[controller]/[action]")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
[Route("[controller]/[action]")]
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
次のコードでは、コントローラーに が適用されます。
[Route("[controller]/[action]")]
public class HomeController : Controller
{
[Route("~/")]
[Route("/Home")]
[Route("~/Home/Index")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
上のコードの メソッド テンプレートで、ルート テンプレートの先頭に または を追加する必要があります。 または で始まるアクションに適用されるルート テンプレートは、コントローラーに適用されるルート テンプレートと結合されません。
ルート テンプレートの選択については、ルート テンプレートの優先順位に関する説明を参照してください。
予約済みルーティング名
次のキーワードは、コントローラーまたは ページを使用する場合の予約ルート パラメーター名です。
actionareacontrollerhandlerpage
属性ルーティングでルート パラメーターとして を使用すると、一般的なエラーが発生します。 この選択により、URL の生成に一貫性がなく、混乱を招く動作になります。
public class MyDemo2Controller : Controller
{
[Route("/articles/{page}")]
public IActionResult ListArticles(int page)
{
return ControllerContext.MyDisplayRouteInfo(page);
}
}
URL 生成では、これらの特殊なパラメーター名を使用して、URL 生成操作が ページまたはコントローラーを参照しているかどうかを判断します。
次のキーワードは、 ビューまたは ページのコンテキストで予約されています。
pageusingnamespaceinjectsectioninheritsmodeladdTagHelperremoveTagHelper
これらのキーワードは、リンクの生成、モデル バインド パラメーター、または最上位のプロパティには使用しないでください。
HTTP 動詞テンプレート
ASP.NET Coreには、次の HTTP 動詞テンプレートが含まれています。
- [HttpGet]
- [HttpPost]
- [HttpPut]
- [HttpDelete]
- [HttpHead]
- [HttpPatch]
ルート テンプレート
ASP.NET Coreには、次のルート テンプレートが含まれています。
- HTTP 動詞テンプレートのすべては、ルート テンプレートにもなります。
- [Route]
HTTP 動詞属性を使用した属性ルーティング
次のようなコントローラーがあるとします。
[Route("api/[controller]")]
[ApiController]
public class Test2Controller : ControllerBase
{
[HttpGet] // GET /api/test2
public IActionResult ListProducts()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")] // GET /api/test2/xyz
public IActionResult GetProduct(string id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpGet("int/{id:int}")] // GET /api/test2/int/3
public IActionResult GetIntProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpGet("int2/{id}")] // GET /api/test2/int2/3
public IActionResult GetInt2Product(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
上のコードでは以下の操作が行われます。
- 各アクションには 属性が含まれているので、HTTP GET 要求への一致のみを制限します。
- アクションには テンプレートが含まれているため、はコントローラーの テンプレートに追加されます。 メソッドのテンプレートは 。 したがって、このアクションは、フォーム 、 、 などの GET 要求にのみ一致します。
[HttpGet("{id}")] // GET /api/test2/xyz public IActionResult GetProduct(string id) { return ControllerContext.MyDisplayRouteInfo(id); } - アクションは テンプレートを組み込みます。 テンプレート の部分で、整数に変換できる文字列に ルート値を制限します。 への GET リクエストは、
- このアクションと一致しません。
- 404 Not Found エラーを返します。
[HttpGet("int/{id:int}")] // GET /api/test2/int/3 public IActionResult GetIntProduct(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
- アクションには、テンプレートの が含まれますが、整数に変換できる値に を制限しません。 への GET リクエストは、
- このルートと一致します。
- モデル バインドは整数への の変換に失敗します。 メソッドの パラメーターは整数です。
- モデル バインドが を整数に変換できなかったため、 を返します。
[HttpGet("int2/{id}")] // GET /api/test2/int2/3 public IActionResult GetInt2Product(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
属性ルーティングでは、、、 などの 属性を使用できます。 HTTP 動詞属性はすべて、ルート テンプレートを受け入れます。 次の例では、同じルート テンプレートと一致する 2 つのアクションが示されています。
[ApiController]
public class MyProductsController : ControllerBase
{
[HttpGet("/products3")]
public IActionResult ListProducts()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpPost("/products3")]
public IActionResult CreateProduct(MyProduct myProduct)
{
return ControllerContext.MyDisplayRouteInfo(myProduct.Name);
}
}
URL パス を使用すると、
- が の場合に アクションが実行されます。
- が の場合に アクションが実行されます。
API を構築する場合、アクションがすべての HTTP メソッドを受け入れるため、アクション メソッドでを使用する必要がある場合はほとんどありません。 API でサポートされている内容を正確に確認するには、より具体的な HTTP 動詞属性 を使用します。 API のクライアントは、パスやHTTP動詞が特定の論理操作にどのように対応するかを理解していることが期待されています。
API では、属性ルーティングを使用して、HTTP 動詞で操作を表現するリソースのセットとしてアプリの機能をモデル化する必要があります。 この設計は、同じ論理リソースに対する GET や POST などの多くの操作で同じ URL を使用することを意味します。 属性ルーティングは、API のパブリック エンドポイント レイアウトを慎重に設計するために必要な制御レベルを提供します。
属性ルートは特定のアクションに適用されるため、ルート テンプレート定義の一部として簡単にパラメーターを必須にできます。 次の例では、 は URL パスの一部として必須です。
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
アクションは、
- のような URL パスで実行されます。
- URL パス では実行されません。
[Consumes] 属性を使用すると、アクションでサポートされる要求のコンテンツの種類を制限できます。 詳細については、「Consumes 属性を使ってサポートされる要求のコンテンツの種類を定義する」を参照してください。
ルート テンプレートと関連するオプションについて詳しくは、「ルーティング」をご覧ください。
の詳細については、ApiController 属性に関する記事を参照してください。
ルート名
次のコードでは、 のルート名を定義しています。
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
ルート名を使用して、特定のルートに基づいて URL を生成します。 ルート名:
- ルーティングの URL 照合動作には影響しません。
- URL の生成にのみ使用されます。
ルート名は、アプリケーション全体で一意である必要があります。
上のコードと、 パラメーターを省略可能 () として定義する規則の既定ルートを比較します。 API を厳密に指定するこの機能には、 と を異なるアクションに対してディスパッチできるといった利点があります。
属性ルートの組み合わせ
属性ルーティングの繰り返しを減らすには、コントローラーのルート属性と個々のアクションのルート属性を組み合わせます。 コントローラーで定義したルート テンプレートは、アクションのルート テンプレートの前に付加されます。 コントローラーにルート属性を配置すると、コントローラー 内のすべての アクションで属性ルーティングが使用されます。
[ApiController]
[Route("products")]
public class ProductsApiController : ControllerBase
{
[HttpGet]
public IActionResult ListProducts()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
前の例の場合:
- URL パス は と一致させることができます。
- URL パス は と一致させることができます。
どちらのアクションも、 属性でマークされているため、HTTP だけと一致します。
アクションに適用するルートテンプレートで、「...」で始まるものは、コントローラーに適用するルートテンプレートと組み合わせることはできません。 次の例は、既定ルートと同様の URL パスのセットと一致します。
[Route("Home")]
public class HomeController : Controller
{
[Route("")]
[Route("Index")]
[Route("/")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
[Route("About")]
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
次の表では、上のコードの 属性について説明します。
| Attribute | との組み合わせ | ルート テンプレートを定義 |
|---|---|---|
[Route("")] |
Yes | "Home" |
[Route("Index")] |
Yes | "Home/Index" |
[Route("/")] |
No | "" |
[Route("About")] |
Yes | "Home/About" |
属性ルートの順序
ルーティングによってツリーが構築され、すべてのエンドポイントが同時に照合されます。
- ルート エントリは、理想的な順序に配置されているかのように動作します。
- 最も具体的なルートは、より一般的なルートの前に実行される可能性があります。
たとえば、 のような属性ルートは、 のような属性ルートよりも具体的です。 ルートの優先順位は、より具体的なので、既定では高くなります。 従来の ルーティングを使用することで、開発者は必要な順序でルートを配置する必要があります。
属性ルートでは、 プロパティを使用して順番を構成できます。 フレームワークで提供されるすべてのルート属性には、 が含まれます。 ルートは、 プロパティの昇順に従って処理されます。 既定の順序は です。 でルートを設定する場合、順序が設定されていないルートの前に実行されます。 でルートを設定する場合、既定のルート順序の後に実行されます。
には依存。 アプリの URL 空間で正しくルーティングするために明示的な順序値が必要な場合、クライアントの混乱を招く可能性があります。 一般に、属性ルーティングは URL 照合で正しいルートを選びます。 URL の生成に使われる既定の順序がうまくいかない場合は、通常、オーバーライドとしてルート名を使う方が、 プロパティを適用するより簡単です。
次の 2 つのコントローラーを考え、両方で と一致するルートを定義します。
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult Index(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult About(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
public class MyDemoController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult MyIndex(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult MyAbout(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
上記のコードを使用してリクエストすると、次のような例外がスローされます。
AmbiguousMatchException: The request matched multiple endpoints. Matches:
WebMvcRouting.Controllers.HomeController.Index
WebMvcRouting.Controllers.MyDemoController.MyIndex
ルート属性の 1 つに を追加すると、あいまいさが解決されます。
[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
return ControllerContext.MyDisplayRouteInfo();
}
上のコードでは、 は エンドポイントを実行します。 を取得するには、 を要求します。 Note:
- 上記のコードは、ルーティング設計が不適切な例です。 プロパティを示します。
- プロパティはあいまいさを解決するだけです。 そのテンプレートは一致できません。 テンプレートは削除することをお勧めします。
ページでのルートの順序については、「 ページのルートとアプリの規則: ルートの順序」を参照してください。
場合によっては、あいまいなルートで HTTP 500 エラーが返されます。 ログを使用して、 の原因となるエンドポイントを確認します。
ルート テンプレートでのトークンの置換 ([controller]、[action]、[area])
便宜上、属性ルートでは 、トークン を角かっこ (、 ) で囲むことでトークンの置換がサポートされます。 トークン 、 、および は、ルートを定義するアクションのアクション名、エリア名、コントローラー名の値に置き換えられます。
[Route("[controller]/[action]")]
public class Products0Controller : Controller
{
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
上のコードでは以下の操作が行われます。
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
- と一致します
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
- と一致します
トークンの置換は、属性ルートを構築する最後の手順として行われます。 上の例では、次のコードと同じ動作が実行されます。
public class Products20Controller : Controller
{
[HttpGet("[controller]/[action]")] // Matches '/Products20/List'
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("[controller]/[action]/{id}")] // Matches '/Products20/Edit/{id}'
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
英語以外の言語でこれをお読みの方で、コードのコメントを母国語で見たい場合は、このGitHubディスカッションのトピックでお知らせください。
属性ルートと継承を組み合わせることもできます。 この組み合わせは、トークンの置換を使用する場合に強力です。 トークンの置換は、属性ルートで定義されているルート名にも適用されます。 では、アクションごとに一意のルート名が生成されます。
[ApiController]
[Route("api/[controller]/[action]", Name = "[controller]_[action]")]
public abstract class MyBase2Controller : ControllerBase
{
}
public class Products11Controller : MyBase2Controller
{
[HttpGet] // /api/products11/list
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")] // /api/products11/edit/3
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
リテラル トークン置換区切り文字 または と一致させるためには、その文字を繰り返すことでエスケープします ( または )。
パラメーター トランスフォーマーを使用してトークンの置換をカスタマイズする
パラメーター トランスフォーマーを使用して、トークンの置換をカスタマイズできます。 パラメーター トランスフォーマーは を実装し、パラメーターの値を変換します。 たとえば、 パラメーター トランスフォーマーでは、 のルート値が に変更されます。
using System.Text.RegularExpressions;
public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
public string? TransformOutbound(object? value)
{
if (value == null) { return null; }
return Regex.Replace(value.ToString()!,
"([a-z])([A-Z])",
"$1-$2",
RegexOptions.CultureInvariant,
TimeSpan.FromMilliseconds(100)).ToLowerInvariant();
}
}
は、次のようなアプリケーション モデルの規則です。
- アプリケーション内のすべての属性ルートにパラメーター トランスフォーマーを適用します。
- 属性ルート トークンの値が置き換えられるとき、それらの値をカスタマイズします。
public class SubscriptionManagementController : Controller
{
[HttpGet("[controller]/[action]")]
public IActionResult ListAll()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
上の メソッドでは と一致します。
は、オプションとして登録されています。
using Microsoft.AspNetCore.Mvc.ApplicationModels;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews(options =>
{
options.Conventions.Add(new RouteTokenTransformerConvention(
new SlugifyParameterTransformer()));
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
slug の定義については、 Slug の MDN Web ドキュメントを参照してください。
Warning
を使用して信頼できない入力を処理するときは、タイムアウトを渡します。 悪意のあるユーザーが に入力を提供して、サービス拒否攻撃を行う可能性があります。 ASP.NET CoreフレームワークのAPIが
複数の属性ルート
属性ルーティングでは、同じアクションに到達する複数のルートの定義がサポートされています。 これの最も一般的な使用方法は、次の例で示すように、"既定の規則ルート" の動作を模倣することです。
[Route("[controller]")]
public class Products13Controller : Controller
{
[Route("")] // Matches 'Products13'
[Route("Index")] // Matches 'Products13/Index'
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
コントローラーに複数のルート属性を配置すると、それぞれが、アクション メソッドの各ルート属性と結合します。
[Route("Store")]
[Route("[controller]")]
public class Products6Controller : Controller
{
[HttpPost("Buy")] // Matches 'Products6/Buy' and 'Store/Buy'
[HttpPost("Checkout")] // Matches 'Products6/Checkout' and 'Store/Checkout'
public IActionResult Buy()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
すべての HTTP 動詞ルート制約は、 を実装します。
を実装する複数のルート属性が 1 つのアクションに配置されている場合:
- 各アクション制約は、コントローラーに適用されたルート テンプレートと組み合わされます。
[Route("api/[controller]")]
public class Products7Controller : ControllerBase
{
[HttpPut("Buy")] // Matches PUT 'api/Products7/Buy'
[HttpPost("Checkout")] // Matches POST 'api/Products7/Checkout'
public IActionResult Buy()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
アクションで複数のルートを使用すると便利で強力な場合があります。アプリの URL 空間の基本的な状態を維持し、明確に定義しておくことをお勧めします。 アクションで複数のルートを使うのは、必要な場合 (既存のクライアントをサポートする、など) だけにしてください。
属性ルートの省略可能なパラメーター、既定値、制約を指定する
属性ルートでは、省略可能なパラメーター、既定値、および制約の指定に関して、規則ルートと同じインライン構文がサポートされています。
public class Products14Controller : Controller
{
[HttpPost("product14/{id:int}")]
public IActionResult ShowProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
上のコードでは、 はルート制約を適用します。 アクションは、 のような URL パスによってのみ照合されます。 ルート テンプレートの部分 は、そのセグメントを整数のみに制限します。
ルート テンプレートの構文の詳細については、「 ルート テンプレート リファレンス」を参照してください。
IRouteTemplateProvider を使用したカスタム ルート属性
すべてのルート属性は を実装します。 ASP.NET Core ランタイム:
- アプリの起動時に、コントローラー クラスとアクション メソッドの属性を検索します。
- を実装する属性を使用して、ルートの初期セットを構築します。
を実装して、カスタム ルート属性を定義します。 各 では、カスタム ルート テンプレート、順序、名前を使って 1 つのルートを定義できます。
public class MyApiControllerAttribute : Attribute, IRouteTemplateProvider
{
public string Template => "api/[controller]";
public int? Order => 2;
public string Name { get; set; } = string.Empty;
}
[MyApiController]
[ApiController]
public class MyTestApiController : ControllerBase
{
// GET /api/MyTestApi
[HttpGet]
public IActionResult Get()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
上の メソッドでは が返されます。
アプリケーション モデルを使用した属性ルートのカスタマイズ
アプリケーション モデルは、
- で起動時に作成されるオブジェクト モデルです。
- アプリでアクションをルーティングして実行するために ASP.NET Coreによって使用されるすべてのメタデータが含まれます。
アプリケーション モデルには、ルート属性から収集されるすべてのデータが含まれます。 ルート属性からのデータは、 実装によって提供されます。 Conventions:
- アプリケーション モデルを変更して、ルーティングの動作をカスタマイズするために、記述できます。
- アプリの起動時に読み込まれます。
このセクションでは、アプリケーション モデルを使用してルーティングをカスタマイズする基本的な例を示します。 次のコードでは、ルートがプロジェクトのフォルダー構造とほぼ同じになるようにします。
public class NamespaceRoutingConvention : Attribute, IControllerModelConvention
{
private readonly string _baseNamespace;
public NamespaceRoutingConvention(string baseNamespace)
{
_baseNamespace = baseNamespace;
}
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
var namespc = controller.ControllerType.Namespace;
if (namespc == null)
return;
var template = new StringBuilder();
template.Append(namespc, _baseNamespace.Length + 1,
namespc.Length - _baseNamespace.Length - 1);
template.Replace('.', '/');
template.Append("/[controller]/[action]/{id?}");
foreach (var selector in controller.Selectors)
{
selector.AttributeRouteModel = new AttributeRouteModel()
{
Template = template.ToString()
};
}
}
}
次のコードでは、属性でルーティングされているコントローラーに 規則が適用されないようにします。
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
たとえば、次のコントローラーでは を使用しません。
[Route("[controller]/[action]/{id?}")]
public class ManagersController : Controller
{
// /managers/index
public IActionResult Index()
{
var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
return Content($"Index- template:{template}");
}
public IActionResult List(int? id)
{
var path = Request.Path.Value;
return Content($"List- Path:{path}");
}
}
メソッド:
- コントローラーが属性でルーティングされている場合は、何も実行しません。
- に基づいてコントローラー テンプレートを設定します。ベース は削除されます。
は で適用できます。
using My.Application.Controllers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews(options =>
{
options.Conventions.Add(
new NamespaceRoutingConvention(typeof(HomeController).Namespace!));
});
var app = builder.Build();
たとえば、次のようなコントローラーがあるとします。
using Microsoft.AspNetCore.Mvc;
namespace My.Application.Admin.Controllers
{
public class UsersController : Controller
{
// GET /admin/controllers/users/index
public IActionResult Index()
{
var fullname = typeof(UsersController).FullName;
var template =
ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
var path = Request.Path.Value;
return Content($"Path: {path} fullname: {fullname} template:{template}");
}
public IActionResult List(int? id)
{
var path = Request.Path.Value;
return Content($"Path: {path} ID:{id}");
}
}
}
上のコードでは以下の操作が行われます。
- ベース は です。
- 上のコントローラーの完全な名前は です。
- によって、コントローラー テンプレートが に設定されます。
は、コントローラーの属性としても適用できます。
[NamespaceRoutingConvention("My.Application")]
public class TestController : Controller
{
// /admin/controllers/test/index
public IActionResult Index()
{
var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
var actionname = ControllerContext.ActionDescriptor.ActionName;
return Content($"Action- {actionname} template:{template}");
}
public IActionResult List(int? id)
{
var path = Request.Path.Value;
return Content($"List- Path:{path}");
}
}
混合ルーティング: 属性ルーティングと規則ルーティング
ASP.NET Coreアプリでは、従来のルーティングと属性ルーティングの使用を混在させることができます。 通常、HTML ページをブラウザーに提供するコントローラーには従来のルートを使用し、 API を提供するコントローラーには属性ルーティングを使用します。
アクションは、規則的にルーティングされるか、または属性でルーティングされます。 コントローラーまたはアクションにルートを設定すると、属性ルーティングになります。 従来のルートを介して属性ルートを定義するアクションには到達できません。また、その逆も同様です。 コントローラーの任意のルート属性により、コントローラー属性のすべてのアクションがルーティングされます。
属性ルーティングと規則ルーティングで、同じルーティング エンジンが使用されます。
特殊文字を含むルーティング
特殊文字を含むルーティングは、予期しない結果になる可能性があります。 たとえば、次のアクション メソッドを含むコントローラーについて考えます。
[HttpGet("{id?}/name")]
public async Task<ActionResult<string>> GetName(string id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null || todoItem.Name == null)
{
return NotFound();
}
return todoItem.Name;
}
に次のエンコードされた値が含まれる場合、予期しない結果が発生する可能性があります。
| ASCII | Encoded |
|---|---|
/ |
%2F |
|
+ |
ルート パラメーターは常に URL でデコードされるとは限りません。 この問題は今後対処される可能性があります。 詳細については、このGitHubの問題を参照してください。
URL の生成とアンビエント値
アプリでは、ルーティングの URL 生成機能を使って、アクションへの URL リンクを生成できます。 URL を生成すると URL をハードコーディングする必要がなくなり、コードの堅牢性と保守性が向上します。 このセクションでは、MVC によって提供される URL 生成機能について説明します。URL 生成のしくみに関する基本だけを取り上げます。 URL の生成について詳しくは、「ルーティング」をご覧ください。
インターフェイスは、MVC と URL 生成のルーティングの間にあるインフラストラクチャの基盤となる要素です。 のインスタンスは、コントローラー、ビュー、およびビュー コンポーネントの プロパティを使って使用できます。
次の例の インターフェイスは、別のアクションへの URL を生成するために プロパティを介して使われています。
public class UrlGenerationController : Controller
{
public IActionResult Source()
{
// Generates /UrlGeneration/Destination
var url = Url.Action("Destination");
return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
}
public IActionResult Destination()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
アプリで既定の従来のルートを使用する場合、 変数の値は URL パス文字列 。 ルーティングでは、次の組み合わせによってこの URL パスが作成されます。
- アンビエント値と呼ばれる、現在の要求のルート値。
- に値を渡し、それらの値をルートテンプレートに置き換える。
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}
result: /UrlGeneration/Destination
ルート テンプレートの各ルート パラメーターの値は、一致する名前と値およびアンビエント値によって置換されます。 値を持たないルート パラメーターは、
- 既定値がある場合は、それを使用できます。
- 省略可能な場合はスキップできます (たとえば、ルート テンプレート の )。
必須のルート パラメーターに対応する値がない場合、URL の生成は失敗します。 ルートの URL 生成が失敗した場合、すべてのルートが試されるまで、または一致が見つかるまで、次のルートが試されます。
上の の例では、規則ルーティングを想定しています。 URL 生成は属性ルーティングでも同じように動作しますが、概念は異なります。 従来のルーティングでは、
- ルート値は、テンプレートを展開するために使用されます。
- と のルート値は通常、そのテンプレートに表示されます。 これは、ルーティングによって一致した URL が規則に準拠しているために機能します。
次の例では、属性ルーティングが使用されています。
public class UrlGenerationAttrController : Controller
{
[HttpGet("custom")]
public IActionResult Source()
{
var url = Url.Action("Destination");
return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
}
[HttpGet("custom/url/to/destination")]
public IActionResult Destination()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
上のコードの アクションによって、 が生成されます。
LinkGenerator は、IUrlHelper の代わりに、ASP.NET Core 3.0 で追加されました。 は同様のより柔軟な機能を提供します。 の各メソッドには、 に対応するメソッド ファミリが存在します。
アクション名による URL の生成
Url.Action、LinkGenerator.GetPathByAction、および関連するすべてのオーバーロードは、コントローラー名とアクション名を指定して、ターゲット エンドポイントを生成するように設計されています。
を使用すると、ランタイムは {parameter1} および {parameter2} の現在のルート値を提供します。
- との値は、アンビエント値と値の両方の一部です。 メソッドは、常に と の現在の値を使い、現在のアクションにルーティングする URL パスを生成します。
ルーティングの際に、URL の生成時に指定されなかった情報を、アンビエント値を使って埋めようとします。 アンビエント値 を使用する のようなルートを考えてみましょう。
- ルーティングには、URL を生成するための十分な情報が含まれており、追加の値は必要ありません。
- すべてのルート パラメーターに値があるため、ルーティングには十分な情報があります。
値 が追加された場合、
- 値 は無視されます。
- 生成された URL パスは です。
警告: URL パスは階層的です。 上の例で、値 が追加されている場合、
- 値 は両方とも無視されます。
- URL生成に失敗するような値がもう存在しなくなりました。
- URL を生成するには、 と の必要な値を指定する必要があります。
既定のルート では、この問題が発生する可能性があります。 では常に と の値が明示的に指定されているため、実際にはこの問題はめったに起こりません。
Url.Action のいくつかのオーバーロードでは、ルート値のオブジェクトを受け取って、 と 以外のルート パラメーターの値を提供します。 ルート値オブジェクトは、 と共によく使用されます。 たとえば、「 」のように入力します。 ルート値オブジェクトは、
- 慣例により、通常は匿名型のオブジェクトです。
- または POCO になります。
ルート パラメーターと一致しない追加のルート値は、クエリ文字列に含まれます。
public IActionResult Index()
{
var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
return Content(url!);
}
上のコードによって が生成されます。
次のコードでは、絶対 URL が生成されます。
public IActionResult Index2()
{
var url = Url.Action("Buy", "Products", new { id = 17 }, protocol: Request.Scheme);
// Returns https://localhost:5001/Products/Buy/17
return Content(url!);
}
絶対 URL を作成するには、次のいずれかのオプションを使用します。
- を受け入れるオーバーロード。 たとえば、上のコードを使用します。
- 既定で絶対 URI を生成する LinkGenerator.GetUriByAction。
ルートによる URL の生成
上のコードでは、コントローラーとアクション名を渡すことによる URL の生成を示しました。 では、Url.RouteUrl ファミリ メソッドも提供されています。 これらのメソッドは、Url.Action と似ていますが、 と の現在の値をルート値にコピーしません。 の最も一般的な使用方法は、次のとおりです。
- URL を生成するルート名を指定します。
- 通常、コントローラーまたはアクション名は指定しません。
public class UrlGeneration2Controller : Controller
{
[HttpGet("")]
public IActionResult Source()
{
var url = Url.RouteUrl("Destination_Route");
return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
}
[HttpGet("custom/url/to/destination2", Name = "Destination_Route")]
public IActionResult Destination()
{
return ControllerContext.MyDisplayRouteInfo();
}
次の ファイルでは、 への HTML リンクが生成されます。
<h1>Test Links</h1>
<ul>
<li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>
HTML および での URL の生成
は メソッドの Html.BeginForm と Html.ActionLink を提供し、それぞれ と 要素を生成します。 これらのメソッドは Url.Action メソッドを使って URL を生成するので、同じような引数を受け取ります。 の コンパニオンは、同様の機能を持つ と です。
TagHelper は、 TagHelper と TagHelper を使って URL を生成します。 これらはどちらも、実装に を使います。 詳細については、「 フォームのタグ ヘルパー」を参照してください。
ビュー内では、 は、上記のメソッドでカバーされていないアドホック URL 生成の プロパティを通じて使用できます。
アクションの結果での URL の生成
上記の例は、コントローラーで を使用する方法を示しています。 コントローラーでの最も一般的な使用方法は、アクションの結果の一部として URL を生成する方法です。
基底クラス および では、別のアクションを参照するアクション結果用の便利なメソッドが提供されています。 一般的な使用方法の 1 つは、ユーザー入力を受け付けた後でリダイレクトする方法です。
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, Customer customer)
{
if (ModelState.IsValid)
{
// Update DB with new details.
ViewData["Message"] = $"Successful edit of customer {id}";
return RedirectToAction("Index");
}
return View(customer);
}
や のようなアクション結果ファクトリ メソッドは、 のメソッドに似たパターンに従います。
専用規則ルートの特殊なケース
規則ルーティングでは、専用規則ルートと呼ばれる特殊なルート定義を使うことができます。 次の例の という名前のルートが専用規則ルートです。
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
上記のルート定義を使用、 ルートを使用して URL パスを生成しますが、その理由は何ですか? ルート値を使用して URL を生成するのに十分であると推測するかもしれません。そして、結果は XXX になるでしょう。
特定の従来ルートは、URL の生成においてルートが過度に欲張りになるのを防ぐために、対応するルート パラメーターを持たないデフォルト値の特別な動作に依存します。 この場合、既定値は であり、 と はどちらもルート パラメーターとして表示されません。 ルーティングが URL 生成を実行するとき、指定された値は既定値と一致する必要があります。 値 は と一致しないため、 を使った URL の生成は失敗します。 そして、ルーティングはフォールバックして を試み、これは成功します。
Areas
区分は、関連する機能を個別のグループとしてまとめるために使用される MVC の機能です。
- コントローラー アクションのルーティング名前空間。
- ビュー用のフォルダー構造。
区分を使うと、アプリは同じ名前の複数のコントローラーを持つことができます (ただし、区分が異なる場合)。 区分を使い、別のルート パラメーター を および に追加することで、ルーティングのための階層を作成します。 このセクションでは、ルーティングと区分がどのように相互作用するかについて説明します。 区分をビューで使用する方法の詳細については、区分に関する記事を参照してください。
次の例では、既定の規則ルートと、 という名前の に対する ルートを使うように、MVC を構成しています。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapAreaControllerRoute("blog_route", "Blog",
"Manage/{controller}/{action}/{id?}");
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
app.Run();
上のコードでは、 を作成するために が呼び出されています。 2 番目のパラメーター は区分名です。
のような URL パスを照合すると、 ルートではルート値 が生成されます。 ルートの値は、の既定値から取得されます。 によって作成されたルートは、次のコードと同じです。
app.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
defaults: new { area = "Blog" }, constraints: new { area = "Blog" });
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
指定されたエリア名を使用して、既定値と制約の両方を利用してルートを作成します。この場合、指定されたエリア名を使用します。 既定値は、ルートが常に を生成することを保証し、制約には URL 生成に 値が必要です。
従来のルーティングは順序に依存しています。 一般に、エリアのないルートよりも具体的であるため、エリアを含むルートを以前に配置します。
上の例を使うと、ルート値 は次のアクションと一致します。
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace1
{
[Area("Blog")]
public class UsersController : Controller
{
// GET /manage/users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
[Area] 属性は、コントローラーが区分の一部であることを示します。 このコントローラーは、この 区分にあります。 属性を持たないコントローラーは領域のメンバーではなく、ルーティングによってルート値が指定されている場合は一致しません。 次の例では、リストの最初のコントローラーだけがルート値 と一致できます。
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace1
{
[Area("Blog")]
public class UsersController : Controller
{
// GET /manage/users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace2
{
// Matches { area = Zebra, controller = Users, action = AddUser }
[Area("Zebra")]
public class UsersController : Controller
{
// GET /zebra/users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace3
{
// Matches { area = string.Empty, controller = Users, action = AddUser }
// Matches { area = null, controller = Users, action = AddUser }
// Matches { controller = Users, action = AddUser }
public class UsersController : Controller
{
// GET /users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
完全のために、各コントローラーの名前空間を次に示します。 上のコントローラーで同じ名前空間が使用されている場合は、コンパイラ エラーが発生します。 クラスの名前空間は、MVC のルーティングに対して影響を与えません。
最初の 2 つのコントローラーは区分のメンバーであり、それぞれの区分名が ルート値によって提供された場合にのみ一致します。 3 番目のコントローラーはどの区分のメンバーでもなく、ルーティングによって の値が提供されない場合にのみ一致します。
"値なし" との一致では、 の値がないことは、 の値が null または空の文字列であることと同じです。
区分の内部でアクションを実行するとき、 のルート値は、URL の生成に使うルーティングのアンビエント値として利用できます。 つまり、既定では、次の例で示すように、区分は URL 生成時に「固定」として機能します。
app.MapAreaControllerRoute(name: "duck_route",
areaName: "Duck",
pattern: "Manage/{controller}/{action}/{id?}");
app.MapControllerRoute(name: "default",
pattern: "Manage/{controller=Home}/{action=Index}/{id?}");
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace4
{
[Area("Duck")]
public class UsersController : Controller
{
// GET /Manage/users/GenerateURLInArea
public IActionResult GenerateURLInArea()
{
// Uses the 'ambient' value of area.
var url = Url.Action("Index", "Home");
// Returns /Manage/Home/Index
return Content(url);
}
// GET /Manage/users/GenerateURLOutsideOfArea
public IActionResult GenerateURLOutsideOfArea()
{
// Uses the empty value for area.
var url = Url.Action("Index", "Home", new { area = "" });
// Returns /Manage
return Content(url);
}
}
}
次のコードでは への URL が生成されます。
public class HomeController : Controller
{
public IActionResult About()
{
var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
return Content($"URL: {url}");
}
アクション定義
NonAction 属性が設定されているものを除き、コントローラーのパブリック メソッドはアクションです。
サンプル コード
- MyDisplayRouteInfo は Rick.Docs.Samples.RouteInfo NuGet パッケージによって提供され、ルート情報が表示されます。
- サンプル コードを表示またはダウンロードします (ダウンロード方法)。
デバッグ診断
詳細なルーティング診断出力を行うには、 を に設定してください。 環境で、でログ レベルを設定します。
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
ASP.NET Core コントローラーは、ルーティング middleware を使用して受信要求の URL を照合し、それらを actions にマップします。 ルート テンプレート:
- スタートアップ コードまたは属性で定義されています。
- URL パスとアクションの照合方法が記述されています。
- リンクの URL を生成するために使用されます。 生成されたリンクは通常、応答で返されます。
アクションは、規則的にルーティングされるか、または属性でルーティングされます。 コントローラーまたはアクションにルートを配置すると、そのルートは属性でルーティングされるようになります。 詳しくは、「混合ルーティング」をご覧ください。
このドキュメントは次のとおりです。
- MVC とルーティングの間の相互作用について説明します。
- 一般的な MVC アプリでルーティング機能を利用する方法。
- 次の両方について説明します。
- 従来のルーティングは通常、コントローラーとビューで使用されます。
- API で使用される。 API のルーティングに主に関心がある場合は、「 API の属性ルーティング」セクションに進んでください。
- 高度なルーティングについて詳しくは、ルーティングに関する記事を参照してください。
- エンドポイント ルーティングと呼ばれる、ASP.NET Core 3.0 で追加された既定のルーティング システムを参照します。 互換性を確保するために、以前のバージョンのルーティングでコントローラーを使用することができます。 手順については、2.2-3.0 の移行ガイドを参照してください。 レガシ ルーティング システムについては、このドキュメントの 2.2 バージョンを参照してください。
従来のルートの設定
通常、 従来のルーティングを使用する場合は、次の例のようなコードがあります。
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
への呼び出しの内部で、単一ルートを作成するために が使用されます。 この単一ルートの名前は ルートです。 コントローラーとビューを使用するほとんどのアプリでは、 ルートと同様のルート テンプレートが使用されます。 API では、属性ルーティングを使用する必要があります。
ルート テンプレート :
のような URL パスと一致します
パスをトークン化して、ルートの値 を抽出します。 アプリに という名前のコントローラーと アクションがある場合、ルート値の抽出が一致します。
public class ProductsController : Controller { public IActionResult Details(int id) { return ControllerContext.MyDisplayRouteInfo(id); } }MyDisplayRouteInfo は Rick.Docs.Samples.RouteInfo NuGet パッケージによって提供され、ルート情報が表示されます。
モデルは、 の値をバインドして、 パラメーターを に設定します。 詳細については、「 モデル バインド」を参照してください。
は、 を既定の として定義します。
は、 を既定の として定義します。
の文字 は、 を省略可能として定義します。
既定および省略可能のルート パラメーターは、URL パスに存在していなくても一致します。 ルート テンプレートの構文の詳細については、「 ルート テンプレート リファレンス」を参照してください。
URL パス と一致します。
ルート値 を生成します。
と の値に、既定値が使用されます。 URL パスに対応するセグメントがないため、 は値を生成しません。 と アクションが存在する場合のみ、 は一致します。
public class HomeController : Controller
{
public IActionResult Index() { ... }
}
上記のコントローラー定義とルート テンプレートを使用すると、 アクションは次の URL パスに対して実行されます。
/Home/Index/17/Home/Index/Home/
URL パス では、ルート テンプレートの既定の コントローラーと アクションが使用されます。 URL パス では、ルート テンプレートの既定の アクションが使用されます。
便利なメソッド :
endpoints.MapDefaultControllerRoute();
Replaces:
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
Important
ルーティングは、 、 、および ミドルウェアを使用して構成されます。 コントローラーを使用するには、
- の内部で を呼び出して、属性でルーティングされたコントローラーをマップします。
- またはを呼び出して、従来のルーティング コントローラーと属性ルーティング コントローラーの両方をマップします。
従来のルーティング
コントローラーとビューで従来のルーティングを使用します。 ルートは次のとおりです。
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
上記のコードは、 従来のルートの例です。 これを "規則ルーティング" と呼ぶのは、それが URL パスの "規則" を作成するためです。
- 最初のパス セグメント は、コントローラー名にマップします。
- 2 番目のセグメント は、アクション名にマップします。
- 3 番目のセグメント は、省略可能な に使われます。 の によって、省略可能になります。 それはモデルエンティティにマップされます。
この ルートを使用すると、URL パスは次のようになります。
- は、 アクションにマップします。
- は、 にマップし、通常 パラメーターを 17 にバインドします。
このマッピング:
- コントローラーとアクションの名前にのみ基づきます。
- 名前空間、ソース ファイルの場所、またはメソッドのパラメーターには基づきません。
既定のルートで従来のルーティングを使用することで、アクションごとに新しい URL パターンを作成する必要はありません。 CRUD スタイルのアクションを使用するアプリの場合、コントローラー間で URL の一貫性を保つことは、
- コードを簡略化するのに役立ちます。
- UI の予測可能性を向上させます。
Warning
ルート テンプレートは、 を省略可能として定義します。 アクションは、URL の一部として指定された省略可能な ID なしで実行できます。 通常、URL から を省略すると、次のようになります。
- モデル バインドは、 を に設定します。
- データベース照合 でエンティティが見つかりません。
属性ルーティングを使うと、ID が必須のアクションと必須ではないアクションをきめ細かく制御できます。 慣例に従って、ドキュメントには などの省略可能なパラメーターが正しい使用法で使われる可能性が高い場合に記載されています。
ほとんどのアプリでは、URL を読みやすくてわかりやすいものにするために、基本的なでわかりやすいルーティング スキームを選択する必要があります。 既定の規則ルート :
- 基本的でわかりやすいルーティング スキームをサポートしています。
- UI ベースのアプリの便利な開始点となります。
- 多くの Web UI アプリに必要な唯一のルート テンプレートになります。 大規模な Web UI アプリの場合でも、大抵は区分を使用するもう 1 つのルートがあれば十分です。
と では、
- それぞれが呼び出された順序に基づいて、それぞれのエンドポイントに順序値が自動的に割り当てられます。
ASP.NET Core 3.0 以降のエンドポイント ルーティング:
- ルートの概念がありません。
- 拡張性の実行に対する順序付けの保証は提供されません。 すべてのエンドポイントが一度に処理されます。
ログを有効にすると、 など、組み込みのルーティング実装で要求を照合するしくみを確認できます。
属性ルーティングについては、このドキュメントの後で説明します。
複数の従来のルート
と の呼び出しをさらに追加することで、 の内部に複数のを追加できます。 これにより、次のように、複数の規則を定義したり、特定のアクションに専用の規則ルートを追加したりすることができます。
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
endpoints.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
上の例の が、専用の規則ルートです。 これは、次の理由から、専用の従来のルートです。
- 規則ルーティングを使用します。
- 特定のアクション専用です。
ルート テンプレート には、パラメーターとして と が含まれていないためです。
- 既定値 しか指定できません。
- このルートは常にアクション にマップされます。
、、および は、ブログ ルートに一致する唯一の URL パスです。
前の例の場合:
- 最初に追加するため、このルートはそのルートよりも一致の優先度が高くなります。
- これは、URL の一部として記事名を付けるのが一般的な Slug スタイルのルーティングの例です。
Warning
ASP.NET Core 3.0 以降では、ルーティングでは次の処理は行われません。
- "ルート" と呼ばれる概念の定義。 はミドルウェア パイプラインにルートの照合を追加します。 ミドルウェアによって、アプリで定義されているエンドポイントのセットが調べられ、要求に基づいて最適な一致が選択されます。
- や のような機能拡張の実行順序に関する保証。
詳細については、ルーティングに関する記事を参照してください。
従来のルーティング順序
従来のルーティングは、アプリが定義するアクションとコントローラーの組み合わせにのみ一致します。 この方法により、従来のルートが重なる場合が簡略化されます。 、、およびを使用してルートを追加すると、エンドポイントは、これらのメソッドを呼び出した順序に基づいて注文値を自動的に取得します。 一覧の前に表示されたルートからの一致は、優先度が高くなります。 従来のルーティングは順序によって異なります。 一般に、エリアのないルートよりも具体的なルートであるため、前の場所にルートを配置します。 たとえば、キャッチオールルートパラメーターを持つ専用の従来のルートは、ルートが過度に適用されることがあります。 他のルートで照合することを意図した URL を貪欲ルートが一致させてしまうことがあります。 意図しないルートまで一致しないようにするため、一致範囲が広いルートを後ろに置きます。
Warning
ルーティングでバグが原因で、キャッチオール パラメーターがルートと正しく一致しない可能性があります。 このバグの影響を受けるアプリには、次の特性があります。
- キャッチオール ルート (たとえば、)
- キャッチオール ルートが、一致すべき要求と一致しません。
- 他のルートを削除すると、キャッチオール ルートが機能し始めます。
GitHubのバグ18677および16579の例を参照して、このバグが発生したケースを確認してください。
このバグのオプトイン修正は、.NET Core 3.1.301 以降の SDK に含まれています。 次のコードにより、このバグを修正する内部スイッチが設定されます。
public static void Main(string[] args)
{
AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior",
true);
CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.
あいまいなアクションの解決
ルーティング経由で 2 つのエンドポイントが一致する場合、ルーティングでは次のいずれかの手順を実行する必要があります。
- 最適な候補を選択します。
- 例外をスローします。
例えば次が挙げられます。
public class Products33Controller : Controller
{
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpPost]
public IActionResult Edit(int id, Product product)
{
return ControllerContext.MyDisplayRouteInfo(id, product.name);
}
}
}
上のコントローラーでは、一致する次の 2 つのアクションが定義されています。
- URL パス
- ルート データ
これは MVC コントローラーの一般的なパターンです。
- には、製品を編集するフォームが表示されます。
- は、ポストされたフォームを処理します。
正しいルートを解決するため、
- は、要求が HTTP の場合に選択されます。
- は、HTTP 動詞がそれ以外の場合に選択されます。 は通常、 を介して呼び出されます。
() は、要求の HTTP メソッドに基づいて選択できるようルーティングに提供されます。 は、 よりも の一致を向上させます。
のような属性の役割を理解することが重要です。 同様の属性は、他の HTTP 動詞に対して定義されます。 規則ルーティングでは、アクションが表示フォームや送信フォームのワークフローの一部になっている場合、複数のアクションが同じアクション名を使うのはよくあることです。 たとえば、2 つの編集アクションのメソッドに関する説明を参照してください。
ルーティングで最適な候補を選択できない場合は、 がスローされ、一致する複数のエンドポイントが一覧表示されます。
従来のルート名
次の例の文字列 と は、規則ルート名です。
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
endpoints.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
ルート名は、ルートに論理名を付けます。 名前付きルートは、URL の生成に使用できます。 ルートの順序指定によって URL の生成が複雑になる場合に、名前付きルートを使用すると、URL の作成が大幅に簡略化されます。 ルート名は、アプリケーション全体で一意である必要があります。
ルート名:
- URL の照合や要求の処理に影響を与えません。
- URL の生成にのみ使用されます。
ルート名の概念は、ルーティングで IEndpointNameMetadata として表されます。 ルート名とエンドポイント名は、
- 交換可能です。
- ドキュメントとコードでどちらが使用されるかは、説明されている API によって異なります。
API の属性ルーティング
API では、属性ルーティングを使用して、HTTP 動詞で操作を表現するリソースのセットとしてアプリの機能をモデル化する必要があります。
属性ルーティングでは、属性のセットを使ってアクションをルート テンプレートに直接マップします。 次の コードは、 API では一般的であり、次のサンプルで使用されます。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
上のコードでは、 内で が呼び出され、属性ルーティング コントローラーがマップされます。
次に例を示します。
- は、既定ルート が一致するのと同様に、一連の URL に一致します。
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult Index(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult About(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
アクションは、、、、またはのいずれかの URL パスに対して実行されます。
この例では、属性ルーティングと規則ルーティングでのプログラミングの大きな違いが強調して示されています。 属性ルーティングでは、ルートを指定するために追加の入力が必要です。 既定の規則ルートは、より簡潔にルートを処理します。 ただし、属性ルーティングでは、各アクションに適用するルート テンプレートを正確に制御できます (そして制御する必要があります)。
属性ルーティングでは、トークンの置換が使用されていない限り、コントローラーとアクションの名前はアクションの照合で果たす役割はありません。 次の例は、前の例と同じ URL と一致します。
public class MyDemoController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult MyIndex(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult MyAbout(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
次のコードでは、 と のトークン置換が使用されます。
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("[controller]/[action]")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
[Route("[controller]/[action]")]
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
次のコードでは、コントローラーに が適用されます。
[Route("[controller]/[action]")]
public class HomeController : Controller
{
[Route("~/")]
[Route("/Home")]
[Route("~/Home/Index")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
上のコードの メソッド テンプレートで、ルート テンプレートの先頭に または を追加する必要があります。 または で始まるアクションに適用されるルート テンプレートは、コントローラーに適用されるルート テンプレートと結合されません。
ルート テンプレートの選択については、ルート テンプレートの優先順位に関する説明を参照してください。
予約済みルーティング名
次のキーワードは、コントローラーまたは ページを使用する場合の予約ルート パラメーター名です。
actionareacontrollerhandlerpage
属性ルーティングでルート パラメーターとして を使用すると、一般的なエラーが発生します。 この選択により、URL の生成に一貫性がなく、混乱を招く動作になります。
public class MyDemo2Controller : Controller
{
[Route("/articles/{page}")]
public IActionResult ListArticles(int page)
{
return ControllerContext.MyDisplayRouteInfo(page);
}
}
URL 生成では、これらの特殊なパラメーター名を使用して、URL 生成操作が ページまたはコントローラーを参照しているかどうかを判断します。
次のキーワードは、 ビューまたは ページのコンテキストで予約されています。
pageusingnamespaceinjectsectioninheritsmodeladdTagHelperremoveTagHelper
これらのキーワードは、リンクの生成、モデル バインド パラメーター、または最上位のプロパティには使用しないでください。
HTTP 動詞テンプレート
ASP.NET Coreには、次の HTTP 動詞テンプレートが含まれています。
- [HttpGet]
- [HttpPost]
- [HttpPut]
- [HttpDelete]
- [HttpHead]
- [HttpPatch]
ルート テンプレート
ASP.NET Coreには、次のルート テンプレートが含まれています。
- HTTP 動詞テンプレートのすべては、ルート テンプレートにもなります。
- [Route]
HTTP 動詞属性を使用した属性ルーティング
次のようなコントローラーがあるとします。
[Route("api/[controller]")]
[ApiController]
public class Test2Controller : ControllerBase
{
[HttpGet] // GET /api/test2
public IActionResult ListProducts()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")] // GET /api/test2/xyz
public IActionResult GetProduct(string id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpGet("int/{id:int}")] // GET /api/test2/int/3
public IActionResult GetIntProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[HttpGet("int2/{id}")] // GET /api/test2/int2/3
public IActionResult GetInt2Product(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
上のコードでは以下の操作が行われます。
- 各アクションには 属性が含まれているので、HTTP GET 要求への一致のみを制限します。
- アクションには テンプレートが含まれているため、はコントローラーの テンプレートに追加されます。 メソッドのテンプレートは 。 したがって、このアクションは、フォーム 、 、 などの GET 要求にのみ一致します。
[HttpGet("{id}")] // GET /api/test2/xyz public IActionResult GetProduct(string id) { return ControllerContext.MyDisplayRouteInfo(id); } - アクションは テンプレートを組み込みます。 テンプレート の部分で、整数に変換できる文字列に ルート値を制限します。 への GET リクエストは、
- このアクションと一致しません。
- 404 Not Found エラーを返します。
[HttpGet("int/{id:int}")] // GET /api/test2/int/3 public IActionResult GetIntProduct(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
- アクションには、テンプレートの が含まれますが、整数に変換できる値に を制限しません。 への GET リクエストは、
- このルートと一致します。
- モデル バインドは整数への の変換に失敗します。 メソッドの パラメーターは整数です。
- モデル バインドが を整数に変換できなかったため、 を返します。
[HttpGet("int2/{id}")] // GET /api/test2/int2/3 public IActionResult GetInt2Product(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
属性ルーティングでは、、、 などの 属性を使用できます。 HTTP 動詞属性はすべて、ルート テンプレートを受け入れます。 次の例では、同じルート テンプレートと一致する 2 つのアクションが示されています。
[ApiController]
public class MyProductsController : ControllerBase
{
[HttpGet("/products3")]
public IActionResult ListProducts()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpPost("/products3")]
public IActionResult CreateProduct(MyProduct myProduct)
{
return ControllerContext.MyDisplayRouteInfo(myProduct.Name);
}
}
URL パス を使用すると、
- が の場合に アクションが実行されます。
- が の場合に アクションが実行されます。
API を構築する場合、アクションですべての HTTP メソッドが受け入れられるため、アクション メソッドで を使用する必要があるのはまれです。 より具体的な HTTP 動詞属性を使って、API がサポートするものを正確に指定することをお勧めします。 API のクライアントは、パスやHTTP動詞が特定の論理操作にどのように対応するかを理解していることが期待されています。
API では、属性ルーティングを使用して、HTTP 動詞で操作を表現するリソースのセットとしてアプリの機能をモデル化する必要があります。 この設計は、同じ論理リソースに対する GET や POST などの多くの操作で同じ URL を使用することを意味します。 属性ルーティングは、API のパブリック エンドポイント レイアウトを慎重に設計するために必要な制御レベルを提供します。
属性ルートは特定のアクションに適用されるため、ルート テンプレート定義の一部として簡単にパラメーターを必須にできます。 次の例では、 は URL パスの一部として必須です。
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
アクションは、
- のような URL パスで実行されます。
- URL パス では実行されません。
[Consumes] 属性を使用すると、アクションでサポートされる要求のコンテンツの種類を制限できます。 詳細については、「Consumes 属性を使ってサポートされる要求のコンテンツの種類を定義する」を参照してください。
ルート テンプレートと関連するオプションについて詳しくは、「ルーティング」をご覧ください。
の詳細については、ApiController 属性に関する記事を参照してください。
ルート名
次のコードでは、 のルート名を定義しています。
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
ルート名を使用して、特定のルートに基づいて URL を生成します。 ルート名:
- ルーティングの URL 照合動作には影響しません。
- URL の生成にのみ使用されます。
ルート名は、アプリケーション全体で一意である必要があります。
上のコードと、 パラメーターを省略可能 () として定義する規則の既定ルートを比較します。 API を厳密に指定するこの機能には、 と を異なるアクションに対してディスパッチできるといった利点があります。
属性ルートの組み合わせ
属性ルーティングの繰り返しを減らすには、コントローラーのルート属性と個々のアクションのルート属性を組み合わせます。 コントローラーで定義したルート テンプレートは、アクションのルート テンプレートに前置されます。 コントローラーにルート属性を配置すると、コントローラー 内のすべての アクションで属性ルーティングが使用されます。
[ApiController]
[Route("products")]
public class ProductsApiController : ControllerBase
{
[HttpGet]
public IActionResult ListProducts()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
前の例の場合:
- URL パス は と一致させることができます。
- URL パス は と一致させることができます。
どちらのアクションも、 属性でマークされているため、HTTP だけと一致します。
アクションに適用し、 または で始まるルート テンプレートは、コントローラーに適用するルート テンプレートと組み合わせられません。 次の例は、既定ルートと同様の URL パスのセットと一致します。
[Route("Home")]
public class HomeController : Controller
{
[Route("")]
[Route("Index")]
[Route("/")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
[Route("About")]
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
次の表では、上のコードの 属性について説明します。
| Attribute | との組み合わせ | ルート テンプレートを定義 |
|---|---|---|
[Route("")] |
Yes | "Home" |
[Route("Index")] |
Yes | "Home/Index" |
[Route("/")] |
No | "" |
[Route("About")] |
Yes | "Home/About" |
属性ルートの順序
ルーティングによってツリーが構築され、すべてのエンドポイントが同時に照合されます。
- ルート エントリは、理想的な順序に配置されているかのように動作します。
- 最も具体的なルートは、より一般的なルートの前に実行される可能性があります。
たとえば、 のような属性ルートは、 のような属性ルートよりも具体的です。 ルートの優先順位は、より具体的なので、既定では高くなります。 規則ルーティングを使うときは、開発者が目的の順序でルートを配置する必要があります。
属性ルートでは、 プロパティを使用して順番を構成できます。 フレームワークで提供されるすべてのルート属性には、 が含まれます。 ルートは、 プロパティの昇順に従って処理されます。 既定の順序は です。 でルートを設定する場合、順序が設定されていないルートの前に実行されます。 でルートを設定する場合、既定のルート順序の後に実行されます。
には依存。 アプリの URL 空間で正しくルーティングするために明示的な順序値が必要な場合、クライアントの混乱を招く可能性があります。 一般に、属性ルーティングは URL 照合で正しいルートを選びます。 URL の生成に使われる既定の順序がうまくいかない場合は、通常、オーバーライドとしてルート名を使う方が、 プロパティを適用するより簡単です。
次の 2 つのコントローラーを考え、両方で と一致するルートを定義します。
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult Index(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult About(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
public class MyDemoController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]
public IActionResult MyIndex(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
[Route("Home/About")]
[Route("Home/About/{id?}")]
public IActionResult MyAbout(int? id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
上記のコードを使用することによって要求を行うと、次のような例外がスローされます。
AmbiguousMatchException: The request matched multiple endpoints. Matches:
WebMvcRouting.Controllers.HomeController.Index
WebMvcRouting.Controllers.MyDemoController.MyIndex
ルート属性の 1 つに を追加すると、あいまいさが解決されます。
[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
return ControllerContext.MyDisplayRouteInfo();
}
上のコードでは、 は エンドポイントを実行します。 を取得するには、 を要求します。 Note:
- 上記のコードは、ルーティング設計が不適切な例です。 プロパティを示します。
- プロパティはあいまいさを解決するだけです。 そのテンプレートは一致できません。 テンプレートは削除することをお勧めします。
ページでのルートの順序については、「 ページのルートとアプリの規則: ルートの順序」を参照してください。
場合によっては、あいまいなルートで HTTP 500 エラーが返されます。 ログを使用して、 の原因となるエンドポイントを確認します。
ルート テンプレートでのトークンの置換 ([controller]、[action]、[area])
便宜上、属性ルートでは 、トークン を角かっこ (、 ) で囲むことでトークンの置換がサポートされます。 トークン 、 、および は、ルートを定義するアクションのアクション名、エリア名、コントローラー名の値に置き換えられます。
[Route("[controller]/[action]")]
public class Products0Controller : Controller
{
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
上のコードでは以下の操作が行われます。
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
- と一致します
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
- と一致します
トークンの置換は、属性ルートを構築する最後の手順として行われます。 上の例では、次のコードと同じ動作が実行されます。
public class Products20Controller : Controller
{
[HttpGet("[controller]/[action]")] // Matches '/Products20/List'
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("[controller]/[action]/{id}")] // Matches '/Products20/Edit/{id}'
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
英語以外の言語でこれをお読みの方で、コードのコメントを母国語で見たい場合は、このGitHubディスカッションのトピックでお知らせください。
属性ルートと継承を組み合わせることもできます。 この組み合わせは、トークンの置換を使用する場合に強力です。 トークンの置換は、属性ルートで定義されているルート名にも適用されます。 では、アクションごとに一意のルート名が生成されます。
[ApiController]
[Route("api/[controller]/[action]", Name = "[controller]_[action]")]
public abstract class MyBase2Controller : ControllerBase
{
}
public class Products11Controller : MyBase2Controller
{
[HttpGet] // /api/products11/list
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")] // /api/products11/edit/3
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
リテラル トークン置換区切り文字 または と一致させるためには、その文字を繰り返すことでエスケープします ( または )。
パラメーター トランスフォーマーを使用してトークンの置換をカスタマイズする
パラメーター トランスフォーマーを使用して、トークンの置換をカスタマイズできます。 パラメーター トランスフォーマーは を実装し、パラメーターの値を変換します。 たとえば、 パラメーター トランスフォーマーでは、 のルート値が に変更されます。
public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
public string TransformOutbound(object value)
{
if (value == null) { return null; }
return Regex.Replace(value.ToString(),
"([a-z])([A-Z])",
"$1-$2",
RegexOptions.CultureInvariant,
TimeSpan.FromMilliseconds(100)).ToLowerInvariant();
}
}
は、次のようなアプリケーション モデルの規則です。
- アプリケーション内のすべての属性ルートにパラメーター トランスフォーマーを適用します。
- 属性ルート トークンの値が置き換えられるとき、それらの値をカスタマイズします。
public class SubscriptionManagementController : Controller
{
[HttpGet("[controller]/[action]")]
public IActionResult ListAll()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
上の メソッドでは と一致します。
は、 でオプションとして登録されます。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Conventions.Add(new RouteTokenTransformerConvention(
new SlugifyParameterTransformer()));
});
}
slug の定義については、 Slug の MDN Web ドキュメントを参照してください。
Warning
を使用して信頼できない入力を処理するときは、タイムアウトを渡します。 悪意のあるユーザーが に入力を提供して、サービス拒否攻撃を行う可能性があります。 ASP.NET CoreフレームワークのAPIが
複数の属性ルート
属性ルーティングでは、同じアクションに到達する複数のルートの定義がサポートされています。 これの最も一般的な使用方法は、次の例で示すように、"既定の規則ルート" の動作を模倣することです。
[Route("[controller]")]
public class Products13Controller : Controller
{
[Route("")] // Matches 'Products13'
[Route("Index")] // Matches 'Products13/Index'
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
コントローラーに複数のルート属性を配置すると、それぞれが、アクション メソッドの各ルート属性と結合します。
[Route("Store")]
[Route("[controller]")]
public class Products6Controller : Controller
{
[HttpPost("Buy")] // Matches 'Products6/Buy' and 'Store/Buy'
[HttpPost("Checkout")] // Matches 'Products6/Checkout' and 'Store/Checkout'
public IActionResult Buy()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
すべての HTTP 動詞ルート制約は、 を実装します。
を実装する複数のルート属性が 1 つのアクションに配置されている場合:
- 各アクション制約は、コントローラーに適用されたルート テンプレートと組み合わされます。
[Route("api/[controller]")]
public class Products7Controller : ControllerBase
{
[HttpPut("Buy")] // Matches PUT 'api/Products7/Buy'
[HttpPost("Checkout")] // Matches POST 'api/Products7/Checkout'
public IActionResult Buy()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
アクションで複数のルートを使用すると便利で強力な場合があります。アプリの URL 空間の基本的な状態を維持し、明確に定義しておくことをお勧めします。 アクションで複数のルートを使うのは、必要な場合 (既存のクライアントをサポートする、など) だけにしてください。
属性ルートの省略可能なパラメーター、既定値、制約を指定する
属性ルートでは、省略可能なパラメーター、既定値、および制約の指定に関して、規則ルートと同じインライン構文がサポートされています。
public class Products14Controller : Controller
{
[HttpPost("product14/{id:int}")]
public IActionResult ShowProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
上のコードでは、 はルート制約を適用します。 アクションは、 のような URL パスによってのみ照合されます。 ルート テンプレートの部分 は、そのセグメントを整数のみに制限します。
ルート テンプレートの構文の詳細については、「 ルート テンプレート リファレンス」を参照してください。
IRouteTemplateProvider を使用したカスタム ルート属性
すべてのルート属性は を実装します。 ASP.NET Core ランタイム:
- アプリの起動時に、コントローラー クラスとアクション メソッドの属性を検索します。
- を実装する属性を使用して、ルートの初期セットを構築します。
を実装して、カスタム ルート属性を定義します。 各 では、カスタム ルート テンプレート、順序、名前を使って 1 つのルートを定義できます。
public class MyApiControllerAttribute : Attribute, IRouteTemplateProvider
{
public string Template => "api/[controller]";
public int? Order => 2;
public string Name { get; set; }
}
[MyApiController]
[ApiController]
public class MyTestApiController : ControllerBase
{
// GET /api/MyTestApi
[HttpGet]
public IActionResult Get()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
上の メソッドでは が返されます。
アプリケーション モデルを使用した属性ルートのカスタマイズ
アプリケーション モデルは、
- 起動時に作成されるオブジェクト モデルです。
- アプリでアクションをルーティングして実行するために ASP.NET Coreによって使用されるすべてのメタデータが含まれます。
アプリケーション モデルには、ルート属性から収集されるすべてのデータが含まれます。 ルート属性からのデータは、 実装によって提供されます。 Conventions:
- アプリケーション モデルを変更して、ルーティングの動作をカスタマイズするために、記述できます。
- アプリの起動時に読み込まれます。
このセクションでは、アプリケーション モデルを使用してルーティングをカスタマイズする基本的な例を示します。 次のコードでは、ルートがプロジェクトのフォルダー構造とほぼ同じになるようにします。
public class NamespaceRoutingConvention : Attribute, IControllerModelConvention
{
private readonly string _baseNamespace;
public NamespaceRoutingConvention(string baseNamespace)
{
_baseNamespace = baseNamespace;
}
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
var namespc = controller.ControllerType.Namespace;
if (namespc == null)
return;
var template = new StringBuilder();
template.Append(namespc, _baseNamespace.Length + 1,
namespc.Length - _baseNamespace.Length - 1);
template.Replace('.', '/');
template.Append("/[controller]/[action]/{id?}");
foreach (var selector in controller.Selectors)
{
selector.AttributeRouteModel = new AttributeRouteModel()
{
Template = template.ToString()
};
}
}
}
次のコードでは、属性でルーティングされているコントローラーに 規則が適用されないようにします。
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
たとえば、次のコントローラーでは を使用しません。
[Route("[controller]/[action]/{id?}")]
public class ManagersController : Controller
{
// /managers/index
public IActionResult Index()
{
var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
return Content($"Index- template:{template}");
}
public IActionResult List(int? id)
{
var path = Request.Path.Value;
return Content($"List- Path:{path}");
}
}
メソッド:
- コントローラーが属性でルーティングされている場合は、何も実行しません。
- に基づいてコントローラー テンプレートを設定します。ベース は削除されます。
は で適用できます。
namespace My.Application
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Conventions.Add(
new NamespaceRoutingConvention(typeof(Startup).Namespace));
});
}
// Remaining code ommitted for brevity.
たとえば、次のようなコントローラーがあるとします。
using Microsoft.AspNetCore.Mvc;
namespace My.Application.Admin.Controllers
{
public class UsersController : Controller
{
// GET /admin/controllers/users/index
public IActionResult Index()
{
var fullname = typeof(UsersController).FullName;
var template =
ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
var path = Request.Path.Value;
return Content($"Path: {path} fullname: {fullname} template:{template}");
}
public IActionResult List(int? id)
{
var path = Request.Path.Value;
return Content($"Path: {path} ID:{id}");
}
}
}
上のコードでは以下の操作が行われます。
- ベース は です。
- 上のコントローラーの完全な名前は です。
- によって、コントローラー テンプレートが に設定されます。
は、コントローラーの属性としても適用できます。
[NamespaceRoutingConvention("My.Application")]
public class TestController : Controller
{
// /admin/controllers/test/index
public IActionResult Index()
{
var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
var actionname = ControllerContext.ActionDescriptor.ActionName;
return Content($"Action- {actionname} template:{template}");
}
public IActionResult List(int? id)
{
var path = Request.Path.Value;
return Content($"List- Path:{path}");
}
}
混合ルーティング: 属性ルーティングと規則ルーティング
ASP.NET Coreアプリでは、従来のルーティングと属性ルーティングの使用を混在させることができます。 通常、HTML ページをブラウザーに提供するコントローラーには従来のルートを使用し、 API を提供するコントローラーには属性ルーティングを使用します。
アクションは、規則的にルーティングされるか、または属性でルーティングされます。 コントローラーまたはアクションにルートを配置すると、そのルートは属性ルーティングされるようになります。 属性ルートが定義されているアクションには規則ルートでは到達できず、規則ルートが定義されているアクションには属性ルートでは到達できません。 コントローラーの任意のルート属性により、コントローラー属性のすべてのアクションがルーティングされます。
属性ルーティングと規則ルーティングで、同じルーティング エンジンが使用されます。
URL の生成とアンビエント値
アプリでは、ルーティングの URL 生成機能を使って、アクションへの URL リンクを生成できます。 URL を生成すると URL をハードコーディングする必要がなくなり、コードの堅牢性と保守性が向上します。 このセクションでは、MVC によって提供される URL 生成機能について説明します。URL 生成のしくみに関する基本だけを取り上げます。 URL の生成について詳しくは、「ルーティング」をご覧ください。
インターフェイスは、MVC と URL 生成のルーティングの間にあるインフラストラクチャの基盤となる要素です。 のインスタンスは、コントローラー、ビュー、およびビュー コンポーネントの プロパティを使って使用できます。
次の例の インターフェイスは、別のアクションへの URL を生成するために プロパティを介して使われています。
public class UrlGenerationController : Controller
{
public IActionResult Source()
{
// Generates /UrlGeneration/Destination
var url = Url.Action("Destination");
return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
}
public IActionResult Destination()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
アプリで既定の従来のルートを使用する場合、 変数の値は URL パス文字列 。 ルーティングでは、次の組み合わせによってこの URL パスが作成されます。
- アンビエント値と呼ばれる、現在の要求のルート値。
- に値を渡し、それらの値をルートテンプレートに置き換える。
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}
result: /UrlGeneration/Destination
ルート テンプレートの各ルート パラメーターの値は、一致する名前と値およびアンビエント値によって置換されます。 値を持たないルート パラメーターは、
- 既定値がある場合は、それを使用できます。
- 省略可能な場合はスキップできます (たとえば、ルート テンプレート の )。
必須のルート パラメーターに対応する値がない場合、URL の生成は失敗します。 ルートの URL 生成が失敗した場合、すべてのルートが試されるまで、または一致が見つかるまで、次のルートが試されます。
上の の例では、規則ルーティングを想定しています。 URL 生成は属性ルーティングでも同じように動作しますが、概念は異なります。 従来のルーティングでは、
- ルート値は、テンプレートを展開するために使用されます。
- と のルート値は通常、そのテンプレートに表示されます。 これは、ルーティングによって一致した URL が規則に準拠しているために機能します。
次の例では、属性ルーティングが使用されています。
public class UrlGenerationAttrController : Controller
{
[HttpGet("custom")]
public IActionResult Source()
{
var url = Url.Action("Destination");
return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
}
[HttpGet("custom/url/to/destination")]
public IActionResult Destination()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
上のコードの アクションによって、 が生成されます。
LinkGenerator は、IUrlHelper の代わりに、ASP.NET Core 3.0 で追加されました。 は同様のより柔軟な機能を提供します。 の各メソッドには、 に対応するメソッド ファミリが存在します。
アクション名による URL の生成
Url.Action、LinkGenerator.GetPathByAction、および関連するすべてのオーバーロードは、コントローラー名とアクション名を指定して、ターゲット エンドポイントを生成するように設計されています。
を使用すると、ランタイムは {parameter1} および {parameter2} の現在のルート値を提供します。
- との値は、アンビエント値と値の両方の一部です。 メソッドは、常に と の現在の値を使い、現在のアクションにルーティングする URL パスを生成します。
ルーティングの際に、URL の生成時に指定されなかった情報を、アンビエント値を使って埋めようとします。 アンビエント値 を使用する のようなルートを考えてみましょう。
- ルーティングには、URL を生成するための十分な情報が含まれており、追加の値は必要ありません。
- すべてのルート パラメーターに値があるため、ルーティングには十分な情報があります。
値 が追加された場合、
- 値 は無視されます。
- 生成された URL パスは です。
警告: URL パスは階層的です。 上の例で、値 が追加されている場合、
- 値 は両方とも無視されます。
- URL生成に失敗するような値がもう存在しなくなりました。
- URL を生成するには、 と の必要な値を指定する必要があります。
既定のルート では、この問題が発生する可能性があります。 では常に と の値が明示的に指定されているため、実際にはこの問題はめったに起こりません。
Url.Action のいくつかのオーバーロードでは、ルート値のオブジェクトを受け取って、 と 以外のルート パラメーターの値を提供します。 ルート値オブジェクトは、 と共によく使用されます。 たとえば、「 」のように入力します。 ルート値オブジェクトは、
- 慣例により、通常は匿名型のオブジェクトです。
- または POCO になります。
ルート パラメーターと一致しない追加のルート値は、クエリ文字列に含まれます。
public IActionResult Index()
{
var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
return Content(url);
}
上のコードによって が生成されます。
次のコードでは、絶対 URL が生成されます。
public IActionResult Index2()
{
var url = Url.Action("Buy", "Products", new { id = 17 }, protocol: Request.Scheme);
// Returns https://localhost:5001/Products/Buy/17
return Content(url);
}
絶対 URL を作成するには、次のいずれかのオプションを使用します。
- を受け入れるオーバーロード。 たとえば、上のコードを使用します。
- 既定で絶対 URI を生成する LinkGenerator.GetUriByAction。
ルートによる URL の生成
上のコードでは、コントローラーとアクション名を渡すことによる URL の生成を示しました。 では、Url.RouteUrl ファミリ メソッドも提供されています。 これらのメソッドは、Url.Action と似ていますが、 と の現在の値をルート値にコピーしません。 の最も一般的な使用方法は、次のとおりです。
- URL を生成するルート名を指定します。
- 通常、コントローラーまたはアクション名は指定しません。
public class UrlGeneration2Controller : Controller
{
[HttpGet("")]
public IActionResult Source()
{
var url = Url.RouteUrl("Destination_Route");
return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
}
[HttpGet("custom/url/to/destination2", Name = "Destination_Route")]
public IActionResult Destination()
{
return ControllerContext.MyDisplayRouteInfo();
}
次の ファイルでは、 への HTML リンクが生成されます。
<h1>Test Links</h1>
<ul>
<li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>
HTML および での URL の生成
は メソッドの Html.BeginForm と Html.ActionLink を提供し、それぞれ と 要素を生成します。 これらのメソッドは Url.Action メソッドを使って URL を生成するので、同じような引数を受け取ります。 の コンパニオンは、同様の機能を持つ と です。
TagHelper は、 TagHelper と TagHelper を使って URL を生成します。 これらはどちらも、実装に を使います。 詳細については、「 フォームのタグ ヘルパー」を参照してください。
ビュー内では、 は、上記のメソッドでカバーされていないアドホック URL 生成の プロパティを通じて使用できます。
アクションの結果での URL の生成
上記の例は、コントローラーで を使用する方法を示しています。 コントローラーでの最も一般的な使用方法は、アクションの結果の一部として URL を生成する方法です。
基底クラス および では、別のアクションを参照するアクション結果用の便利なメソッドが提供されています。 一般的な使用方法の 1 つは、ユーザー入力を受け付けた後でリダイレクトする方法です。
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, Customer customer)
{
if (ModelState.IsValid)
{
// Update DB with new details.
ViewData["Message"] = $"Successful edit of customer {id}";
return RedirectToAction("Index");
}
return View(customer);
}
や のようなアクション結果ファクトリ メソッドは、 のメソッドに似たパターンに従います。
専用規則ルートの特殊なケース
規則ルーティングでは、専用規則ルートと呼ばれる特殊なルート定義を使うことができます。 次の例の という名前のルートが専用規則ルートです。
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
endpoints.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
上記のルート定義を使用、 ルートを使用して URL パスを生成しますが、その理由は何ですか? ルート値を使用して URL を生成するのに十分であると推測するかもしれません。そして、結果は XXX になるでしょう。
特定の従来ルートは、URL の生成においてルートが過度に欲張りになるのを防ぐために、対応するルート パラメーターを持たないデフォルト値の特別な動作に依存します。 この場合、既定値は であり、 と はどちらもルート パラメーターとして表示されません。 ルーティングが URL 生成を実行するとき、指定された値は既定値と一致する必要があります。 値 は と一致しないため、 を使った URL の生成は失敗します。 そして、ルーティングはフォールバックして を試み、これは成功します。
Areas
区分は、関連する機能を個別のグループとしてまとめるために使用される MVC の機能です。
- コントローラー アクションのルーティング名前空間。
- ビュー用のフォルダー構造。
区分を使うと、アプリは同じ名前の複数のコントローラーを持つことができます (ただし、区分が異なる場合)。 区分を使い、別のルート パラメーター を および に追加することで、ルーティングのための階層を作成します。 このセクションでは、ルーティングと区分がどのように相互作用するかについて説明します。 区分をビューで使用する方法の詳細については、区分に関する記事を参照してください。
次の例では、既定の規則ルートと、 という名前の に対する ルートを使うように、MVC を構成しています。
app.UseEndpoints(endpoints =>
{
endpoints.MapAreaControllerRoute("blog_route", "Blog",
"Manage/{controller}/{action}/{id?}");
endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});
上のコードでは、 を作成するために が呼び出されています。 2 番目のパラメーター は区分名です。
のような URL パスを照合すると、 ルートではルート値 が生成されます。 ルートの値は、の既定値から取得されます。 によって作成されたルートは、次のコードと同じです。
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
defaults: new { area = "Blog" }, constraints: new { area = "Blog" });
endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});
指定されたエリア名を使用して、既定値と制約の両方を利用してルートを作成します。この場合、指定されたエリア名を使用します。 既定値は、ルートが常に を生成することを保証し、制約には URL 生成に 値が必要です。
従来のルーティングは順序に依存しています。 一般に、エリアのないルートよりも具体的であるため、エリアを含むルートを以前に配置します。
上の例を使うと、ルート値 は次のアクションと一致します。
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace1
{
[Area("Blog")]
public class UsersController : Controller
{
// GET /manage/users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
[Area] 属性は、コントローラーが区分の一部であることを示します。 このコントローラーは、この 区分にあります。 属性を持たないコントローラーは領域のメンバーではなく、ルーティングによってルート値が指定されている場合は一致しません。 次の例では、リストの最初のコントローラーだけがルート値 と一致できます。
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace1
{
[Area("Blog")]
public class UsersController : Controller
{
// GET /manage/users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace2
{
// Matches { area = Zebra, controller = Users, action = AddUser }
[Area("Zebra")]
public class UsersController : Controller
{
// GET /zebra/users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace3
{
// Matches { area = string.Empty, controller = Users, action = AddUser }
// Matches { area = null, controller = Users, action = AddUser }
// Matches { controller = Users, action = AddUser }
public class UsersController : Controller
{
// GET /users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
完全のために、各コントローラーの名前空間を次に示します。 上のコントローラーで同じ名前空間が使用されている場合は、コンパイラ エラーが発生します。 クラスの名前空間は、MVC のルーティングに対して影響を与えません。
最初の 2 つのコントローラーは区分のメンバーであり、それぞれの区分名が ルート値によって提供された場合にのみ一致します。 3 番目のコントローラーはどの区分のメンバーでもなく、ルーティングによって の値が提供されない場合にのみ一致します。
"値なし" との一致では、 の値がないことは、 の値が null または空の文字列であることと同じです。
区分の内部でアクションを実行するとき、 のルート値は、URL の生成に使うルーティングのアンビエント値として利用できます。 つまり、既定では、次の例で示すように、区分は URL 生成時に「固定」として機能します。
app.UseEndpoints(endpoints =>
{
endpoints.MapAreaControllerRoute(name: "duck_route",
areaName: "Duck",
pattern: "Manage/{controller}/{action}/{id?}");
endpoints.MapControllerRoute(name: "default",
pattern: "Manage/{controller=Home}/{action=Index}/{id?}");
});
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace4
{
[Area("Duck")]
public class UsersController : Controller
{
// GET /Manage/users/GenerateURLInArea
public IActionResult GenerateURLInArea()
{
// Uses the 'ambient' value of area.
var url = Url.Action("Index", "Home");
// Returns /Manage/Home/Index
return Content(url);
}
// GET /Manage/users/GenerateURLOutsideOfArea
public IActionResult GenerateURLOutsideOfArea()
{
// Uses the empty value for area.
var url = Url.Action("Index", "Home", new { area = "" });
// Returns /Manage
return Content(url);
}
}
}
次のコードでは への URL が生成されます。
public class HomeController : Controller
{
public IActionResult About()
{
var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
return Content($"URL: {url}");
}
アクション定義
NonAction 属性が設定されているものを除き、コントローラーのパブリック メソッドはアクションです。
サンプル コード
- MyDisplayRouteInfo は Rick.Docs.Samples.RouteInfo NuGet パッケージによって提供され、ルート情報が表示されます。
- サンプル コードを表示またはダウンロードします (ダウンロード方法)。
デバッグ診断
詳細なルーティング診断出力を行うには、 を に設定してください。 環境で、でログ レベルを設定します。
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
ASP.NET Core