Perutean ke tindakan pengontrol di ASP.NET Core
Oleh Ryan Nowak, Kirk Larkin, dan Rick Anderson
Catatan
Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.
Peringatan
Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.
Penting
Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.
Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.
ASP.NET Pengontrol Core menggunakan middleware Perutean untuk mencocokkan URL permintaan masuk dan memetakannya ke tindakan. Templat rute:
- Didefinisikan saat startup dalam
Program.cs
atau dalam atribut. - Menjelaskan bagaimana jalur URL dicocokkan dengan tindakan.
- Digunakan untuk menghasilkan URL untuk tautan. Tautan yang dihasilkan biasanya dikembalikan sebagai respons.
Tindakan dirutekan secara konvensional atau dirutekan atribut. Menempatkan rute pada pengontrol atau tindakan membuatnya dirutekan atribut. Lihat Perutean campuran untuk informasi selengkapnya.
Dokumen ini:
- Menjelaskan interaksi antara MVC dan perutean:
- Bagaimana aplikasi MVC khas memanfaatkan fitur perutean.
- Mencakup keduanya:
- Perutean konvensional biasanya digunakan dengan pengontrol dan tampilan.
- Perutean atribut yang digunakan dengan REST API. Jika Anda terutama tertarik pada perutean untuk REST API, lompat ke bagian Perutean atribut untuk REST API .
- Lihat Perutean untuk detail perutean tingkat lanjut.
- Mengacu pada sistem perutean default yang disebut perutean titik akhir. Dimungkinkan untuk menggunakan pengontrol dengan versi perutean sebelumnya untuk tujuan kompatibilitas. Lihat panduan migrasi 2.2-3.0 untuk petunjuknya.
Menyiapkan rute konvensional
Templat ASP.NET Core MVC menghasilkan kode perutean konvensional yang mirip dengan yang berikut ini:
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();
MapControllerRoute digunakan untuk membuat satu rute. Rute tunggal diberi nama default
rute. Sebagian besar aplikasi dengan pengontrol dan tampilan menggunakan templat rute yang mirip default
dengan rute. REST API harus menggunakan perutean atribut.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Templat "{controller=Home}/{action=Index}/{id?}"
rute :
Cocok dengan jalur URL seperti
/Products/Details/5
Mengekstrak nilai
{ controller = Products, action = Details, id = 5 }
rute dengan membuat token jalur. Ekstraksi nilai rute menghasilkan kecocokan jika aplikasi memiliki pengontrol bernamaProductsController
danDetails
tindakan:public class ProductsController : Controller { public IActionResult Details(int id) { return ControllerContext.MyDisplayRouteInfo(id); } }
MyDisplayRouteInfo disediakan oleh paket Rick.Docs.Samples.RouteInfo NuGet dan menampilkan informasi rute.
/Products/Details/5
model mengikat nilaiid = 5
untuk mengatur parameter keid
5
. Lihat Pengikatan Model untuk detail selengkapnya.{controller=Home}
Home
mendefinisikan sebagai defaultcontroller
.{action=Index}
Index
mendefinisikan sebagai defaultaction
.Karakter
?
dalam{id?}
mendefinisikanid
sebagai opsional.- Parameter rute default dan opsional tidak perlu ada di jalur URL untuk kecocokan. Lihat Referensi Templat Rute untuk deskripsi terperinci tentang sintaks templat rute.
Cocok dengan jalur
/
URL .Menghasilkan nilai
{ controller = Home, action = Index }
rute .
Nilai untuk controller
dan action
memanfaatkan nilai default. id
tidak menghasilkan nilai karena tidak ada segmen yang sesuai di jalur URL. /
hanya cocok jika ada HomeController
tindakan dan Index
:
public class HomeController : Controller
{
public IActionResult Index() { ... }
}
Menggunakan definisi pengontrol sebelumnya dan templat rute, HomeController.Index
tindakan dijalankan untuk jalur URL berikut:
/Home/Index/17
/Home/Index
/Home
/
Jalur /
URL menggunakan pengontrol dan Index
tindakan default Home
templat rute. Jalur /Home
URL menggunakan tindakan default Index
templat rute.
Metode MapDefaultControllerRoutekenyamanan :
app.MapDefaultControllerRoute();
Menggantikan:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Penting
Perutean dikonfigurasi menggunakan UseRouting middleware dan UseEndpoints . Untuk menggunakan pengontrol:
- Panggil MapControllers untuk memetakan pengontrol yang dirutekan atribut.
- Panggil MapControllerRoute atau MapAreaControllerRoute, untuk memetakan pengontrol yang dirutekan secara konvensional dan atribut pengontrol yang dirutekan .
Aplikasi biasanya tidak perlu memanggil UseRouting
atau UseEndpoints
. WebApplicationBuilder mengonfigurasi alur middleware yang membungkus middleware yang ditambahkan Program.cs
dengan UseRouting
dan UseEndpoints
. Untuk informasi lebih lanjut, lihat Perutean di ASP.NET Core.
Perutean konvensional
Perutean konvensional digunakan dengan pengontrol dan tampilan. Rute default
:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Sebelumnya adalah contoh rute konvensional. Ini disebut perutean konvensional karena menetapkan konvensi untuk jalur URL:
- Segmen jalur pertama,
{controller=Home}
, memetakan ke nama pengontrol. - Segmen kedua,
{action=Index}
, memetakan ke nama tindakan . - Segmen ketiga,
{id?}
digunakan untuk opsionalid
. in?
{id?}
membuatnya opsional.id
digunakan untuk memetakan ke entitas model.
Dengan menggunakan rute ini default
, jalur URL:
/Products/List
memetakan keProductsController.List
tindakan./Blog/Article/17
memetakan keBlogController.Article
dan biasanya model mengikatid
parameter ke 17.
Pemetaan ini:
- Hanya didasarkan pada pengontrol dan nama tindakan.
- Tidak didasarkan pada namespace, lokasi file sumber, atau parameter metode.
Menggunakan perutean konvensional dengan rute default memungkinkan pembuatan aplikasi tanpa harus membuat pola URL baru untuk setiap tindakan. Untuk aplikasi dengan tindakan gaya CRUD , memiliki konsistensi untuk URL di seluruh pengontrol:
- Membantu menyederhanakan kode.
- Membuat UI lebih mudah diprediksi.
Peringatan
id
dalam kode sebelumnya didefinisikan sebagai opsional oleh templat rute. Tindakan dapat dijalankan tanpa ID opsional yang disediakan sebagai bagian dari URL. Umumnya, kapan id
dihilangkan dari URL:
id
diatur ke0
berdasarkan pengikatan model.- Tidak ada entitas yang ditemukan dalam database yang cocok
id == 0
.
Perutean atribut memberikan kontrol menenangkan untuk membuat ID diperlukan untuk beberapa tindakan dan bukan untuk yang lain. Menurut konvensi, dokumentasi menyertakan parameter opsional seperti id
saat kemungkinan akan muncul dalam penggunaan yang benar.
Sebagian besar aplikasi harus memilih skema perutean dasar dan deskriptif sehingga URL dapat dibaca dan bermakna. Rute {controller=Home}/{action=Index}/{id?}
konvensional default :
- Mendukung skema perutean dasar dan deskriptif.
- Adalah titik awal yang berguna untuk aplikasi berbasis UI.
- Adalah satu-satunya templat rute yang diperlukan untuk banyak aplikasi UI web. Untuk aplikasi UI web yang lebih besar, rute lain yang menggunakan Area sering kali merupakan semua yang diperlukan.
MapControllerRoute dan MapAreaRoute :
- Secara otomatis menetapkan nilai pesanan ke titik akhir mereka berdasarkan urutan yang dipanggil.
Perutean titik akhir di ASP.NET Core:
- Tidak memiliki konsep rute.
- Tidak memberikan jaminan pemesanan untuk eksekusi ekstensibilitas, semua titik akhir diproses sekaligus.
Aktifkan Pengelogan untuk melihat bagaimana implementasi perutean bawaan, seperti Route, mencocokkan permintaan.
Perutean atribut dijelaskan nanti dalam dokumen ini.
Beberapa rute konvensional
Beberapa rute konvensional dapat dikonfigurasi dengan menambahkan lebih banyak panggilan ke MapControllerRoute dan MapAreaControllerRoute. Melakukannya memungkinkan menentukan beberapa konvensi, atau menambahkan rute konvensional yang didedikasikan untuk tindakan tertentu, seperti:
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Rute blog
dalam kode sebelumnya adalah rute konvensional khusus. Ini disebut rute konvensional khusus karena:
Karena controller
dan action
tidak muncul di templat "blog/{*article}"
rute sebagai parameter:
- Mereka hanya dapat memiliki nilai
{ controller = "Blog", action = "Article" }
default . - Rute ini selalu memetakan ke tindakan
BlogController.Article
.
/Blog
, /Blog/Article
, dan /Blog/{any-string}
adalah satu-satunya jalur URL yang cocok dengan rute blog.
Contoh sebelumnya:
blog
rute memiliki prioritas yang lebih tinggi untuk kecocokandefault
daripada rute karena ditambahkan terlebih dahulu.- Adalah contoh perutean gaya Slug di mana biasanya memiliki nama artikel sebagai bagian dari URL.
Peringatan
Di ASP.NET Core, perutean tidak:
- Tentukan konsep yang disebut rute.
UseRouting
menambahkan pencocokan rute ke alur middleware. MiddlewareUseRouting
melihat kumpulan titik akhir yang ditentukan dalam aplikasi, dan memilih kecocokan titik akhir terbaik berdasarkan permintaan. - Berikan jaminan tentang urutan eksekusi ekstensibilitas seperti IRouteConstraint atau IActionConstraint.
Lihat Perutean untuk bahan referensi tentang perutean.
Urutan perutean konvensional
Perutean konvensional hanya cocok dengan kombinasi tindakan dan pengontrol yang ditentukan oleh aplikasi. Ini dimaksudkan untuk menyederhanakan kasus di mana rute konvensional tumpang tindih.
Menambahkan rute menggunakan MapControllerRoute, MapDefaultControllerRoute, dan MapAreaControllerRoute secara otomatis menetapkan nilai pesanan ke titik akhir mereka berdasarkan urutan yang dipanggil. Kecocokan dari rute yang muncul sebelumnya memiliki prioritas yang lebih tinggi. Perutean konvensional bergantung pada pesanan. Secara umum, rute dengan area harus ditempatkan lebih awal karena lebih spesifik daripada rute tanpa area. Rute konvensional khusus dengan parameter rute catch-all seperti {*article}
dapat membuat rute terlalu serakah, yang berarti bahwa rute tersebut cocok dengan URL yang Anda maksudkan untuk dicocokkan dengan rute lain. Letakkan rute serakah nanti di tabel rute untuk mencegah kecocokan serakah.
Peringatan
Parameter catch-all mungkin salah mencocokkan rute karena bug dalam perutean. Aplikasi yang terpengaruh oleh bug ini memiliki karakteristik berikut:
- Rute catch-all, misalnya,
{**slug}"
- Rute catch-all gagal mencocokkan permintaan yang harus cocok.
- Menghapus rute lain membuat rute catch-all mulai berfungsi.
Lihat bug GitHub 18677 dan 16579 misalnya kasus yang mengenai bug ini.
Perbaikan keikutsertaan untuk bug ini terkandung dalam .NET Core 3.1.301 SDK dan yang lebih baru. Kode berikut menetapkan sakelar internal yang memperbaiki bug ini:
public static void Main(string[] args)
{
AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior",
true);
CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.
Mengatasi tindakan ambigu
Saat dua titik akhir cocok melalui perutean, perutean harus melakukan salah satu hal berikut:
- Pilih kandidat terbaik.
- Tampilkan pengecualian.
Contohnya:
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);
}
}
Pengontrol sebelumnya mendefinisikan dua tindakan yang cocok:
- Jalur URL
/Products33/Edit/17
- Merutekan data
{ controller = Products33, action = Edit, id = 17 }
.
Ini adalah pola umum untuk pengontrol MVC:
Edit(int)
menampilkan formulir untuk mengedit produk.Edit(int, Product)
memproses formulir yang diposting.
Untuk mengatasi rute yang benar:
Edit(int, Product)
dipilih ketika permintaan adalah HTTPPOST
.Edit(int)
dipilih ketika kata kerja HTTP adalah hal lain.Edit(int)
umumnya dipanggil melaluiGET
.
HttpPostAttribute, [HttpPost]
, disediakan untuk perutean sehingga dapat memilih berdasarkan metode HTTP permintaan. Edit(int, Product)
membuat HttpPostAttribute
kecocokan yang lebih baik daripada Edit(int)
.
Penting untuk memahami peran atribut seperti HttpPostAttribute
. Atribut serupa didefinisikan untuk kata kerja HTTP lainnya. Dalam perutean konvensional, umum bagi tindakan untuk menggunakan nama tindakan yang sama saat mereka menjadi bagian dari formulir acara, kirim alur kerja formulir. Misalnya, lihat Memeriksa dua metode tindakan Edit.
Jika perutean tidak dapat memilih kandidat terbaik, sebuah AmbiguousMatchException dilemparkan, mencantumkan beberapa titik akhir yang cocok.
Nama rute konvensional
String "blog"
dan "default"
dalam contoh berikut adalah nama rute konvensional:
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Nama rute memberi rute nama logis. Rute bernama dapat digunakan untuk pembuatan URL. Menggunakan rute bernama menyederhanakan pembuatan URL saat pengurutan rute dapat membuat pembuatan URL menjadi rumit. Nama rute harus lebar aplikasi yang unik.
Nama rute:
- Tidak berdampak pada pencocokan URL atau penanganan permintaan.
- Hanya digunakan untuk pembuatan URL.
Konsep nama rute diwakili dalam perutean sebagai IEndpointNameMetadata. Nama rute istilah dan nama titik akhir:
- Bisa dipertukarkan.
- Mana yang digunakan dalam dokumentasi dan kode tergantung pada API yang dijelaskan.
Perutean atribut untuk REST API
REST API harus menggunakan perutean atribut untuk memodelkan fungsionalitas aplikasi sebagai sekumpulan sumber daya di mana operasi diwakili oleh kata kerja HTTP.
Perutean atribut menggunakan sekumpulan atribut untuk memetakan tindakan langsung ke templat rute. Kode berikut khas untuk REST API dan digunakan dalam sampel berikutnya:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Dalam kode sebelumnya, MapControllers dipanggil untuk memetakan pengontrol rute atribut.
Dalam contoh berikut:
HomeController
cocok dengan sekumpulan URL yang mirip dengan apa yang cocok dengan rute{controller=Home}/{action=Index}/{id?}
konvensional default.
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);
}
}
Tindakan HomeController.Index
dijalankan untuk salah satu jalur /
URL , , /Home
, /Home/Index
atau /Home/Index/3
.
Contoh ini menyoroti perbedaan pemrograman utama antara perutean atribut dan perutean konvensional. Perutean atribut memerlukan lebih banyak input untuk menentukan rute. Rute default konvensional menangani rute dengan lebih tepat. Namun, perutean atribut memungkinkan dan memerlukan kontrol yang tepat tentang templat rute mana yang berlaku untuk setiap tindakan.
Dengan perutean atribut, pengontrol dan nama tindakan tidak memainkan bagian di mana tindakan dicocokkan, kecuali penggantian token digunakan. Contoh berikut cocok dengan URL yang sama dengan contoh sebelumnya:
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);
}
}
Kode berikut menggunakan penggantian token untuk action
dan controller
:
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();
}
}
Kode berikut berlaku [Route("[controller]/[action]")]
untuk pengontrol:
[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();
}
}
Dalam kode sebelumnya, Index
templat metode harus didahului /
atau ~/
ke templat rute. Templat rute diterapkan ke tindakan yang dimulai dengan /
atau ~/
tidak digabungkan dengan templat rute yang diterapkan ke pengontrol.
Lihat Prioritas templat rute untuk informasi tentang pemilihan templat rute.
Nama perutean yang dicadangkan
Kata kunci berikut adalah nama parameter rute yang dipesan saat menggunakan Pengontrol atau Razor Halaman:
action
area
controller
handler
page
Menggunakan page
sebagai parameter rute dengan perutean atribut adalah kesalahan umum. Melakukan itu menghasilkan perilaku yang tidak konsisten dan membingungkan dengan pembuatan URL.
public class MyDemo2Controller : Controller
{
[Route("/articles/{page}")]
public IActionResult ListArticles(int page)
{
return ControllerContext.MyDisplayRouteInfo(page);
}
}
Nama parameter khusus digunakan oleh pembuatan URL untuk menentukan apakah operasi pembuatan URL mengacu pada Razor Halaman atau ke Pengontrol.
Kata kunci berikut dicadangkan dalam konteks Razor tampilan atau Razor Halaman:
page
using
namespace
inject
section
inherits
model
addTagHelper
removeTagHelper
Kata kunci ini tidak boleh digunakan untuk pembuatan tautan, parameter terikat model, atau properti tingkat atas.
Templat kata kerja HTTP
ASP.NET Core memiliki templat kata kerja HTTP berikut:
Templat rute
ASP.NET Core memiliki templat rute berikut:
- Semua templat kata kerja HTTP adalah templat rute.
- [Rute]
Perutean atribut dengan atribut kata kerja Http
Pertimbangkan pengontrol berikut:
[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);
}
}
Dalam kode sebelumnya:
- Setiap tindakan berisi
[HttpGet]
atribut , yang membatasi pencocokan dengan permintaan HTTP GET saja. - Tindakan ini
GetProduct
mencakup"{id}"
templat, oleh karena ituid
ditambahkan ke"api/[controller]"
templat pada pengontrol. Templat metodenya adalah"api/[controller]/{id}"
. Oleh karena itu tindakan ini hanya cocok dengan permintaan GET untuk formulir/api/test2/xyz
,/api/test2/123
,/api/test2/{any string}
, dll.[HttpGet("{id}")] // GET /api/test2/xyz public IActionResult GetProduct(string id) { return ControllerContext.MyDisplayRouteInfo(id); }
- Tindakan
GetIntProduct
berisi"int/{id:int}"
templat. Bagian:int
templat membatasiid
nilai rute ke string yang dapat dikonversi menjadi bilangan bulat. Permintaan GET ke/api/test2/int/abc
:- Tidak cocok dengan tindakan ini.
- Mengembalikan kesalahan 404 Tidak Ditemukan .
[HttpGet("int/{id:int}")] // GET /api/test2/int/3 public IActionResult GetIntProduct(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
- Tindakan
GetInt2Product
berisi{id}
dalam templat, tetapi tidak membatasiid
nilai yang dapat dikonversi menjadi bilangan bulat. Permintaan GET ke/api/test2/int2/abc
:- Cocok dengan rute ini.
- Pengikatan model gagal dikonversi
abc
ke bilangan bulat. Parameterid
metode adalah bilangan bulat. - Mengembalikan Permintaan Buruk 400 karena pengikatan model gagal dikonversi
abc
ke bilangan bulat.[HttpGet("int2/{id}")] // GET /api/test2/int2/3 public IActionResult GetInt2Product(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
Perutean atribut dapat menggunakan HttpMethodAttribute atribut seperti HttpPostAttribute, , HttpPutAttributedan HttpDeleteAttribute. Semua atribut kata kerja HTTP menerima templat rute. Contoh berikut menunjukkan dua tindakan yang cocok dengan templat rute yang sama:
[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);
}
}
Menggunakan jalur /products3
URL :
- Tindakan
MyProductsController.ListProducts
berjalan ketika kata kerja HTTP adalahGET
. - Tindakan
MyProductsController.CreateProduct
berjalan ketika kata kerja HTTP adalahPOST
.
Saat membangun REST API, jarang anda harus menggunakan [Route(...)]
pada metode tindakan karena tindakan menerima semua metode HTTP. Lebih baik menggunakan atribut kata kerja HTTP yang lebih spesifik agar tepat tentang apa yang didukung API Anda. REST Klien API diharapkan mengetahui jalur dan kata kerja HTTP apa yang dipetakan ke operasi logis tertentu.
REST API harus menggunakan perutean atribut untuk memodelkan fungsionalitas aplikasi sebagai sekumpulan sumber daya di mana operasi diwakili oleh kata kerja HTTP. Ini berarti bahwa banyak operasi, misalnya, GET dan POST pada sumber daya logis yang sama menggunakan URL yang sama. Perutean atribut menyediakan tingkat kontrol yang diperlukan untuk merancang tata letak titik akhir publik API dengan hati-hati.
Karena rute atribut berlaku untuk tindakan tertentu, mudah untuk membuat parameter yang diperlukan sebagai bagian dari definisi templat rute. Dalam contoh berikut, id
diperlukan sebagai bagian dari jalur URL:
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Tindakan Products2ApiController.GetProduct(int)
:
- Dijalankan dengan jalur URL seperti
/products2/3
- Tidak dijalankan dengan jalur
/products2
URL .
Dengan atribut [Consumes], suatu tindakan dapat membatasi jenis konten permintaan yang didukung. Untuk informasi selengkapnya, lihat Menentukan jenis konten permintaan yang didukung dengan atribut Konsumsi.
Lihat Perutean untuk deskripsi lengkap templat rute dan opsi terkait.
Untuk informasi selengkapnya tentang [ApiController]
, lihat atribut ApiController.
Nama rute
Kode berikut mendefinisikan nama rute :Products_List
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Nama rute dapat digunakan untuk menghasilkan URL berdasarkan rute tertentu. Nama rute:
- Tidak berdampak pada perilaku perutean pencocokan URL.
- Hanya digunakan untuk pembuatan URL.
Nama rute harus unik di seluruh aplikasi.
Berbeda dengan kode sebelumnya dengan rute default konvensional, yang mendefinisikan id
parameter sebagai opsional ({id?}
). Kemampuan untuk menentukan API dengan tepat memiliki keuntungan, seperti mengizinkan /products
dan /products/5
dikirim ke tindakan yang berbeda.
Menggabungkan rute atribut
Untuk membuat perutean atribut kurang berulang, atribut rute pada pengontrol dikombinasikan dengan atribut rute pada tindakan individual. Templat rute apa pun yang ditentukan pada pengontrol telah ditambahkan ke templat rute pada tindakan. Menempatkan atribut rute pada pengontrol membuat semua tindakan di pengontrol menggunakan perutean atribut.
[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);
}
}
Dalam contoh sebelumnya:
- Jalur
/products
URL dapat cocokProductsApi.ListProducts
- Jalur
/products/5
URL dapat cocokProductsApi.GetProduct(int)
dengan .
Kedua tindakan ini hanya cocok dengan HTTP GET
karena ditandai dengan [HttpGet]
atribut .
Templat rute diterapkan ke tindakan yang dimulai dengan /
atau ~/
tidak digabungkan dengan templat rute yang diterapkan ke pengontrol. Contoh berikut cocok dengan sekumpulan jalur URL yang mirip dengan rute default.
[Route("Home")]
public class HomeController : Controller
{
[Route("")]
[Route("Index")]
[Route("/")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
[Route("About")]
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Tabel berikut menjelaskan [Route]
atribut dalam kode sebelumnya:
Atribut | Menggabungkan dengan [Route("Home")] |
Menentukan templat rute |
---|---|---|
[Route("")] |
Ya | "Home" |
[Route("Index")] |
Ya | "Home/Index" |
[Route("/")] |
Tidak | "" |
[Route("About")] |
Ya | "Home/About" |
Urutan rute atribut
Perutean membangun pohon dan mencocokkan semua titik akhir secara bersamaan:
- Entri rute berulah seolah-olah ditempatkan dalam urutan yang ideal.
- Rute yang paling spesifik memiliki kesempatan untuk dijalankan sebelum rute yang lebih umum.
Misalnya, rute atribut seperti blog/search/{topic}
lebih spesifik daripada rute atribut seperti blog/{*article}
. Rute blog/search/{topic}
memiliki prioritas yang lebih tinggi, secara default, karena lebih spesifik. Dengan menggunakan perutean konvensional, pengembang bertanggung jawab untuk menempatkan rute dalam urutan yang diinginkan.
Rute atribut dapat mengonfigurasi pesanan Order menggunakan properti . Semua atribut rute yang disediakan kerangka kerja meliputi Order
. Rute diproses sesuai dengan jenis properti yang Order
naik. Urutan defaultnya adalah 0
. Mengatur rute menggunakan Order = -1
eksekusi sebelum rute yang tidak mengatur pesanan. Mengatur rute menggunakan Order = 1
eksekusi setelah pemesanan rute default.
Hindari tergantung pada Order
. Jika ruang URL aplikasi memerlukan nilai pesanan eksplisit untuk merutekan dengan benar, kemungkinan membingungkan klien juga. Secara umum, perutean atribut memilih rute yang benar dengan pencocokan URL. Jika urutan default yang digunakan untuk pembuatan URL tidak berfungsi, menggunakan nama rute sebagai penimpaan biasanya lebih sederhana daripada menerapkan Order
properti.
Pertimbangkan dua pengontrol berikut yang keduanya menentukan pencocokan /home
rute :
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);
}
}
/home
Meminta dengan kode sebelumnya melemparkan pengecualian yang mirip dengan yang berikut ini:
AmbiguousMatchException: The request matched multiple endpoints. Matches:
WebMvcRouting.Controllers.HomeController.Index
WebMvcRouting.Controllers.MyDemoController.MyIndex
Order
Menambahkan ke salah satu atribut rute menyelesaikan ambiguitas:
[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
return ControllerContext.MyDisplayRouteInfo();
}
Dengan kode sebelumnya, /home
menjalankan HomeController.Index
titik akhir. Untuk sampai ke MyDemoController.MyIndex
permintaan , /home/MyIndex
. Catatan:
- Kode sebelumnya adalah contoh atau desain perutean yang buruk. Ini digunakan untuk mengilustrasikan
Order
properti . - Properti
Order
hanya menyelesaikan ambiguitas, templat tersebut tidak dapat dicocokkan. Akan lebih baik untuk menghapus[Route("Home")]
templat.
Lihat Razor Rute halaman dan konvensi aplikasi: Urutan rute untuk informasi tentang urutan rute dengan Razor Halaman.
Dalam beberapa kasus, kesalahan HTTP 500 dikembalikan dengan rute ambigu. Gunakan pengelogan untuk melihat titik akhir mana yang menyebabkan AmbiguousMatchException
.
Penggantian token dalam templat rute [pengontrol], [tindakan], [area]
Untuk kenyamanan, rute atribut mendukung penggantian token dengan menyertakan token dalam kurung siku ([
, ]
). Token [action]
, [area]
, dan [controller]
diganti dengan nilai nama tindakan, nama area, dan nama pengontrol dari tindakan tempat rute ditentukan:
[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);
}
}
Dalam kode sebelumnya:
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
- Korek api
/Products0/List
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
- Korek api
/Products0/Edit/{id}
Penggantian token terjadi sebagai langkah terakhir membangun rute atribut. Contoh sebelumnya berpura-pura sama dengan kode berikut:
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);
}
}
Jika Anda membaca ini dalam bahasa selain bahasa Inggris, beri tahu kami dalam masalah diskusi GitHub ini jika Anda ingin melihat komentar kode dalam bahasa asli Anda.
Rute atribut juga dapat dikombinasikan dengan warisan. Ini kuat dikombinasikan dengan penggantian token. Penggantian token juga berlaku untuk nama rute yang ditentukan oleh rute atribut.
[Route("[controller]/[action]", Name="[controller]_[action]")]
menghasilkan nama rute unik untuk setiap tindakan:
[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);
}
}
Untuk mencocokkan pemisah [
penggantian token harfiah atau ]
, keluarkan dengan mengulangi karakter ([[
atau ]]
).
Menggunakan transformator parameter untuk menyesuaikan penggantian token
Penggantian token dapat disesuaikan menggunakan transformator parameter. Transformator parameter mengimplementasikan IOutboundParameterTransformer dan mengubah nilai parameter. Misalnya, transformator parameter kustom SlugifyParameterTransformer
mengubah SubscriptionManagement
nilai rute menjadi subscription-management
:
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();
}
}
RouteTokenTransformerConvention adalah konvensi model aplikasi yang:
- Menerapkan transformator parameter ke semua rute atribut dalam aplikasi.
- Menyesuaikan nilai token rute atribut saat diganti.
public class SubscriptionManagementController : Controller
{
[HttpGet("[controller]/[action]")]
public IActionResult ListAll()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Metode sebelumnya ListAll
cocok /subscription-management/list-all
dengan .
RouteTokenTransformerConvention
terdaftar sebagai opsi:
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();
Lihat dokumen web MDN di Slug untuk definisi Slug.
Peringatan
Saat menggunakan System.Text.RegularExpressions untuk memproses masukan yang tidak tepercaya, berikan batas waktu. Pengguna jahat dapat memberikan masukan ke RegularExpressions
yang menyebabkan Penolakan Serangan Layanan. ASP.NET API kerangka kerja Core yang menggunakan RegularExpressions
batas waktu.
Beberapa rute atribut
Perutean atribut mendukung penentuan beberapa rute yang mencapai tindakan yang sama. Penggunaan yang paling umum adalah meniru perilaku rute konvensional default seperti yang ditunjukkan dalam contoh berikut:
[Route("[controller]")]
public class Products13Controller : Controller
{
[Route("")] // Matches 'Products13'
[Route("Index")] // Matches 'Products13/Index'
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
Menempatkan beberapa atribut rute pada pengontrol berarti masing-masing menggabungkan dengan masing-masing atribut rute pada metode tindakan:
[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();
}
}
Semua batasan rute kata kerja HTTP menerapkan IActionConstraint
.
Saat beberapa atribut rute yang diterapkan IActionConstraint ditempatkan pada tindakan:
- Setiap batasan tindakan dikombinasikan dengan templat rute yang diterapkan ke pengontrol.
[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();
}
}
Menggunakan beberapa rute pada tindakan mungkin tampak berguna dan kuat, lebih baik menjaga ruang URL aplikasi Anda tetap dasar dan terdefinisi dengan baik. Gunakan beberapa rute pada tindakan hanya jika diperlukan, misalnya, untuk mendukung klien yang ada.
Menentukan parameter opsional rute atribut, nilai default, dan batasan
Rute atribut mendukung sintaksis sebaris yang sama dengan rute konvensional untuk menentukan parameter opsional, nilai default, dan batasan.
public class Products14Controller : Controller
{
[HttpPost("product14/{id:int}")]
public IActionResult ShowProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Dalam kode sebelumnya, [HttpPost("product14/{id:int}")]
menerapkan batasan rute. Tindakan Products14Controller.ShowProduct
hanya dicocokkan dengan jalur URL seperti /product14/3
. Bagian {id:int}
templat rute membatasi segmen tersebut hanya untuk bilangan bulat.
Lihat Referensi Templat Rute untuk deskripsi terperinci tentang sintaks templat rute.
Atribut rute kustom menggunakan IRouteTemplateProvider
Semua atribut rute menerapkan IRouteTemplateProvider. Runtime ASP.NET Core:
- Mencari atribut pada kelas pengontrol dan metode tindakan saat aplikasi dimulai.
- Menggunakan atribut yang diterapkan
IRouteTemplateProvider
untuk membangun serangkaian rute awal.
Terapkan IRouteTemplateProvider
untuk menentukan atribut rute kustom. Masing-masing IRouteTemplateProvider
memungkinkan Anda menentukan satu rute dengan templat, pesanan, dan nama rute kustom:
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();
}
}
Metode sebelumnya Get
mengembalikan Order = 2, Template = api/MyTestApi
.
Menggunakan model aplikasi untuk menyesuaikan rute atribut
Model aplikasi:
- Adalah model objek yang dibuat saat startup di
Program.cs
. - Berisi semua metadata yang digunakan oleh ASP.NET Core untuk merutekan dan menjalankan tindakan dalam aplikasi.
Model aplikasi mencakup semua data yang dikumpulkan dari atribut rute. Data dari atribut rute disediakan oleh IRouteTemplateProvider
implementasi. Konvensi:
- Dapat ditulis untuk mengubah model aplikasi untuk menyesuaikan perilaku perutean.
- Dibaca saat pengaktifan aplikasi.
Bagian ini menunjukkan contoh dasar penyesuaian perutean menggunakan model aplikasi. Kode berikut membuat rute kira-kira sejajar dengan struktur folder proyek.
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()
};
}
}
}
Kode berikut mencegah namespace
konvensi diterapkan ke pengontrol yang dirutekan atribut:
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
Misalnya, pengontrol berikut tidak menggunakan NamespaceRoutingConvention
:
[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}");
}
}
Metode NamespaceRoutingConvention.Apply
:
- Tidak melakukan apa pun jika pengontrol dirutekan atributnya.
- Mengatur templat pengontrol berdasarkan
namespace
, dengan basisnamespace
dihapus.
NamespaceRoutingConvention
dapat diterapkan dalam Program.cs
:
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();
Misalnya, pertimbangkan pengontrol berikut:
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}");
}
}
}
Dalam kode sebelumnya:
- Basisnya
namespace
adalahMy.Application
. - Nama lengkap pengontrol sebelumnya adalah
My.Application.Admin.Controllers.UsersController
. - Mengatur
NamespaceRoutingConvention
templat pengontrol keAdmin/Controllers/Users/[action]/{id?
.
NamespaceRoutingConvention
juga dapat diterapkan sebagai atribut pada pengontrol:
[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}");
}
}
Perutean campuran: Perutean atribut vs perutean konvensional
aplikasi ASP.NET Core dapat mencampur penggunaan perutean konvensional dan perutean atribut. Biasanya menggunakan rute konvensional untuk pengontrol yang melayani halaman HTML untuk browser, dan perutean atribut untuk pengontrol yang melayani REST API.
Tindakan dirutekan secara konvensional atau atribut yang dirutekan. Menempatkan rute pada pengontrol atau tindakan membuatnya atribut dirutekan. Tindakan yang menentukan rute atribut tidak dapat dicapai melalui rute konvensional dan sebaliknya. Atribut rute apa pun pada pengontrol membuat semua tindakan dalam atribut pengontrol dirutekan.
Perutean atribut dan perutean konvensional menggunakan mesin perutean yang sama.
Perutean dengan karakter khusus
Perutean dengan karakter khusus dapat menyebabkan hasil yang tidak terduga. Misalnya, pertimbangkan pengontrol dengan metode tindakan berikut:
[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;
}
Ketika string id
berisi nilai yang dikodekan berikut, hasil yang tidak terduga mungkin terjadi:
ASCII | Dikodekan |
---|---|
/ |
%2F |
|
+ |
Parameter rute tidak selalu didekodekan URL. Masalah ini dapat diatasi di masa mendatang. Untuk informasi selengkapnya, lihat masalah GitHub ini;
Pembuatan URL dan nilai sekitar
Aplikasi dapat menggunakan fitur pembuatan URL perutean untuk menghasilkan tautan URL ke tindakan. Menghasilkan URL menghilangkan URL pengodean keras, membuat kode lebih kuat dan dapat dipertahankan. Bagian ini berfokus pada fitur pembuatan URL yang disediakan oleh MVC dan hanya mencakup dasar-dasar cara kerja pembuatan URL. Lihat Perutean untuk deskripsi terperinci tentang pembuatan URL.
Antarmuka IUrlHelper adalah elemen infrastruktur yang mendasar antara MVC dan perutean untuk pembuatan URL. Instans IUrlHelper
tersedia melalui Url
properti di pengontrol, tampilan, dan komponen tampilan.
Dalam contoh berikut, IUrlHelper
antarmuka digunakan melalui Controller.Url
properti untuk menghasilkan URL ke tindakan lain.
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();
}
}
Jika aplikasi menggunakan rute konvensional default, nilai url
variabel adalah string /UrlGeneration/Destination
jalur URL . Jalur URL ini dibuat dengan perutean dengan menggabungkan:
- Nilai rute dari permintaan saat ini, yang disebut nilai sekitar.
- Nilai yang diteruskan ke
Url.Action
dan mengganti nilai tersebut ke dalam templat rute:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}
result: /UrlGeneration/Destination
Setiap parameter rute dalam templat rute memiliki nilainya digantikan dengan mencocokkan nama dengan nilai dan nilai sekitar. Parameter rute yang tidak memiliki nilai dapat:
- Gunakan nilai default jika memilikinya.
- Dilewati jika bersifat opsional. Misalnya,
id
dari templat{controller}/{action}/{id?}
rute .
Pembuatan URL gagal jika parameter rute yang diperlukan tidak memiliki nilai yang sesuai. Jika pembuatan URL gagal untuk rute, rute berikutnya dicoba hingga semua rute telah dicoba atau kecocokan ditemukan.
Contoh Url.Action
sebelumnya mengasumsikan perutean konvensional. Pembuatan URL berfungsi sama dengan perutean atribut, meskipun konsepnya berbeda. Dengan perutean konvensional:
- Nilai rute digunakan untuk memperluas templat.
- Nilai rute untuk
controller
danaction
biasanya muncul dalam templat tersebut. Ini berfungsi karena URL yang cocok dengan perutean mematuhi konvensi.
Contoh berikut menggunakan perutean atribut:
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();
}
}
Tindakan Source
dalam kode sebelumnya menghasilkan custom/url/to/destination
.
LinkGenerator ditambahkan di ASP.NET Core 3.0 sebagai alternatif untuk IUrlHelper
. LinkGenerator
menawarkan fungsionalitas yang serupa tetapi lebih fleksibel. Setiap metode pada IUrlHelper
memiliki keluarga metode yang LinkGenerator
sesuai juga.
Membuat URL menurut nama tindakan
Url.Action, LinkGenerator.GetPathByAction, dan semua kelebihan beban terkait semuanya dirancang untuk menghasilkan titik akhir target dengan menentukan nama pengontrol dan nama tindakan.
Saat menggunakan Url.Action
, nilai rute saat ini untuk controller
dan action
disediakan oleh runtime:
- Nilai
controller
danaction
merupakan bagian dari nilai dan nilai sekitar. MetodeUrl.Action
ini selalu menggunakan nilaiaction
saat ini dan dancontroller
menghasilkan jalur URL yang dirutekan ke tindakan saat ini.
Perutean mencoba menggunakan nilai dalam nilai sekitar untuk mengisi informasi yang tidak disediakan saat membuat URL. Pertimbangkan rute seperti {a}/{b}/{c}/{d}
dengan nilai { a = Alice, b = Bob, c = Carol, d = David }
sekitar :
- Perutean memiliki informasi yang cukup untuk menghasilkan URL tanpa nilai tambahan.
- Perutean memiliki informasi yang cukup karena semua parameter rute memiliki nilai.
Jika nilai { d = Donovan }
ditambahkan:
- Nilai
{ d = David }
diabaikan. - Jalur URL yang dihasilkan adalah
Alice/Bob/Carol/Donovan
.
Peringatan: Jalur URL bersifat hierarkis. Dalam contoh sebelumnya, jika nilai { c = Cheryl }
ditambahkan:
- Kedua nilai
{ c = Carol, d = David }
diabaikan. - Tidak ada lagi nilai untuk
d
pembuatan URL dan gagal. - Nilai yang diinginkan dan
c
d
harus ditentukan untuk menghasilkan URL.
Anda mungkin mengharapkan untuk mengalami masalah ini dengan rute {controller}/{action}/{id?}
default . Masalah ini jarang terjadi dalam praktiknya karena Url.Action
selalu secara eksplisit menentukan nilai controller
dan action
.
Beberapa kelebihan beban Url.Action mengambil objek nilai rute untuk menyediakan nilai untuk parameter rute selain controller
dan action
. Objek nilai rute sering digunakan dengan id
. Contohnya,Url.Action("Buy", "Products", new { id = 17 })
. Objek nilai rute:
- Menurut konvensi biasanya merupakan objek jenis anonim.
- Dapat berupa
IDictionary<>
atau POCO).
Nilai rute tambahan apa pun yang tidak cocok dengan parameter rute dimasukkan ke dalam string kueri.
public IActionResult Index()
{
var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
return Content(url!);
}
Kode sebelumnya menghasilkan /Products/Buy/17?color=red
.
Kode berikut menghasilkan URL absolut:
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!);
}
Untuk membuat URL absolut, gunakan salah satu hal berikut:
- Kelebihan beban yang menerima
protocol
. Misalnya, kode sebelumnya. - LinkGenerator.GetUriByAction, yang menghasilkan URI absolut secara default.
Hasilkan URL menurut rute
Kode sebelumnya menunjukkan pembuatan URL dengan meneruskan pengontrol dan nama tindakan. IUrlHelper
juga menyediakan keluarga metode Url.RouteUrl . Metode ini mirip dengan Url.Action, tetapi tidak menyalin nilai action
saat ini dan controller
ke nilai rute. Penggunaan yang paling umum dari Url.RouteUrl
:
- Menentukan nama rute untuk menghasilkan URL.
- Umumnya tidak menentukan pengontrol atau nama tindakan.
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();
}
File berikut Razor menghasilkan tautan HTML ke Destination_Route
:
<h1>Test Links</h1>
<ul>
<li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>
Hasilkan URL dalam HTML dan Razor
IHtmlHelperHtmlHelper menyediakan metode Html.BeginForm dan Html.ActionLink untuk menghasilkan <form>
elemen dan <a>
masing-masing. Metode ini menggunakan metode Url.Action untuk menghasilkan URL dan mereka menerima argumen serupa. Pendamping Url.RouteUrl
untuk HtmlHelper
adalah Html.BeginRouteForm
dan Html.RouteLink
yang memiliki fungsionalitas serupa.
TagHelpers menghasilkan URL melalui form
TagHelper dan <a>
TagHelper. Kedua penggunaan IUrlHelper
ini untuk implementasinya. Lihat Tag Helper dalam formulir untuk informasi selengkapnya.
Di dalam tampilan, IUrlHelper
tersedia melalui Url
properti untuk pembuatan URL ad-hoc apa pun yang tidak tercakup oleh yang di atas.
Pembuatan URL dalam Hasil Tindakan
Contoh sebelumnya yang diperlihatkan menggunakan IUrlHelper
dalam pengontrol. Penggunaan yang paling umum dalam pengontrol adalah menghasilkan URL sebagai bagian dari hasil tindakan.
Kelas ControllerBase dasar dan Controller menyediakan metode kenyamanan untuk hasil tindakan yang mereferensikan tindakan lain. Salah satu penggunaan umumnya adalah mengalihkan setelah menerima input pengguna:
[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);
}
Tindakan menghasilkan metode pabrik seperti RedirectToAction dan CreatedAtAction mengikuti pola yang mirip dengan metode pada IUrlHelper
.
Kasus khusus untuk rute konvensional khusus
Perutean konvensional dapat menggunakan jenis definisi rute khusus yang disebut rute konvensional khusus. Dalam contoh berikut, rute bernama blog
adalah rute konvensional khusus:
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Menggunakan definisi rute sebelumnya, Url.Action("Index", "Home")
menghasilkan jalur /
URL menggunakan default
rute, tetapi mengapa? Anda mungkin menebak nilai { controller = Home, action = Index }
rute akan cukup untuk menghasilkan URL menggunakan blog
, dan hasilnya adalah /blog?action=Index&controller=Home
.
Rute konvensional khusus mengandalkan perilaku khusus nilai default yang tidak memiliki parameter rute yang sesuai yang mencegah rute terlalu serakah dengan pembuatan URL. Dalam hal ini nilai defaultnya adalah { controller = Blog, action = Article }
, dan tidak controller
juga action
muncul sebagai parameter rute. Saat perutean melakukan pembuatan URL, nilai yang disediakan harus cocok dengan nilai default. Pembuatan URL menggunakan blog
gagal karena nilainya { controller = Home, action = Index }
tidak cocok { controller = Blog, action = Article }
. Perutean kemudian kembali untuk mencoba default
, yang berhasil.
Daerah
Area adalah fitur MVC yang digunakan untuk mengatur fungsionalitas terkait ke dalam grup sebagai terpisah:
- Perutean namespace layanan untuk tindakan pengontrol.
- Struktur folder untuk tampilan.
Menggunakan area memungkinkan aplikasi memiliki beberapa pengontrol dengan nama yang sama, selama memiliki area yang berbeda. Menggunakan area membuat hierarki untuk tujuan perutean dengan menambahkan parameter rute lain, area
ke controller
dan action
. Bagian ini membahas bagaimana perutean berinteraksi dengan area. Lihat Area untuk detail tentang bagaimana area digunakan dengan tampilan.
Contoh berikut mengonfigurasi MVC untuk menggunakan rute konvensional default dan area
rute untuk yang area
bernama Blog
:
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();
Dalam kode sebelumnya, MapAreaControllerRoute dipanggil untuk membuat "blog_route"
. Parameter kedua, "Blog"
, adalah nama area.
Saat mencocokkan jalur URL seperti /Manage/Users/AddUser
, "blog_route"
rute menghasilkan nilai { area = Blog, controller = Users, action = AddUser }
rute . Nilai area
rute dihasilkan oleh nilai default untuk area
. Rute yang dibuat oleh MapAreaControllerRoute
setara dengan yang berikut ini:
app.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
defaults: new { area = "Blog" }, constraints: new { area = "Blog" });
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
MapAreaControllerRoute
membuat rute menggunakan nilai default dan batasan untuk area
menggunakan nama area yang disediakan, dalam hal Blog
ini . Nilai default memastikan bahwa rute selalu menghasilkan { area = Blog, ... }
, batasan memerlukan nilai { area = Blog, ... }
untuk pembuatan URL.
Perutean konvensional bergantung pada pesanan. Secara umum, rute dengan area harus ditempatkan lebih awal karena lebih spesifik daripada rute tanpa area.
Menggunakan contoh sebelumnya, nilai { area = Blog, controller = Users, action = AddUser }
rute cocok dengan tindakan berikut:
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}");
}
}
}
Atribut [Area] adalah yang menunjukkan pengontrol sebagai bagian dari area. Pengontrol ini ada di area tersebut Blog
. Pengontrol tanpa [Area]
atribut bukan anggota area apa pun, dan tidak cocok ketika area
nilai rute disediakan oleh perutean. Dalam contoh berikut, hanya pengontrol pertama yang tercantum yang dapat mencocokkan nilai { area = Blog, controller = Users, action = AddUser }
rute .
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}");
}
}
}
Namespace setiap pengontrol ditampilkan di sini untuk kelengkapan. Jika pengontrol sebelumnya menggunakan namespace yang sama, kesalahan kompilator akan dihasilkan. Namespace layanan kelas tidak berpengaruh pada perutean MVC.
Dua pengontrol pertama adalah anggota area, dan hanya cocok ketika nama area masing-masing disediakan oleh area
nilai rute. Pengontrol ketiga bukan anggota area apa pun, dan hanya dapat cocok ketika tidak ada nilai yang area
disediakan oleh perutean.
Dalam hal tidak ada nilai yang cocok, tidak adanya area
nilai sama seperti jika nilai untuk area
null atau string kosong.
Saat menjalankan tindakan di dalam area, nilai rute untuk area
tersedia sebagai nilai sekitar untuk perutean yang akan digunakan untuk pembuatan URL. Ini berarti bahwa secara default area bertindak lengket untuk pembuatan URL seperti yang ditunjukkan oleh sampel berikut.
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);
}
}
}
Kode berikut menghasilkan URL ke /Zebra/Users/AddUser
:
public class HomeController : Controller
{
public IActionResult About()
{
var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
return Content($"URL: {url}");
}
Definisi tindakan
Metode publik pada pengontrol, kecuali yang memiliki atribut NonAction , adalah tindakan.
Kode Sampel
- MyDisplayRouteInfo disediakan oleh paket Rick.Docs.Samples.RouteInfo NuGet dan menampilkan informasi rute.
- Melihat atau mengunduh kode sampel (cara mengunduh)
Men-debug diagnostik
Untuk output diagnostik perutean terperinci, atur Logging:LogLevel:Microsoft
ke Debug
. Di lingkungan pengembangan, atur tingkat log di appsettings.Development.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
ASP.NET Pengontrol Core menggunakan middleware Perutean untuk mencocokkan URL permintaan masuk dan memetakannya ke tindakan. Templat rute:
- Didefinisikan dalam kode atau atribut startup.
- Menjelaskan bagaimana jalur URL dicocokkan dengan tindakan.
- Digunakan untuk menghasilkan URL untuk tautan. Tautan yang dihasilkan biasanya dikembalikan sebagai respons.
Tindakan dirutekan secara konvensional atau dirutekan atribut. Menempatkan rute pada pengontrol atau tindakan membuatnya dirutekan atribut. Lihat Perutean campuran untuk informasi selengkapnya.
Dokumen ini:
- Menjelaskan interaksi antara MVC dan perutean:
- Bagaimana aplikasi MVC khas memanfaatkan fitur perutean.
- Mencakup keduanya:
- Perutean konvensional biasanya digunakan dengan pengontrol dan tampilan.
- Perutean atribut yang digunakan dengan REST API. Jika Anda terutama tertarik pada perutean untuk REST API, lompat ke bagian Perutean atribut untuk REST API .
- Lihat Perutean untuk detail perutean tingkat lanjut.
- Mengacu pada sistem perutean default yang ditambahkan di ASP.NET Core 3.0, yang disebut perutean titik akhir. Dimungkinkan untuk menggunakan pengontrol dengan versi perutean sebelumnya untuk tujuan kompatibilitas. Lihat panduan migrasi 2.2-3.0 untuk petunjuknya. Lihat versi 2.2 dokumen ini untuk bahan referensi pada sistem perutean warisan.
Menyiapkan rute konvensional
Startup.Configure
biasanya memiliki kode yang mirip dengan yang berikut ini saat menggunakan perutean konvensional:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
Di dalam panggilan ke UseEndpoints, MapControllerRoute digunakan untuk membuat satu rute. Rute tunggal diberi nama default
rute. Sebagian besar aplikasi dengan pengontrol dan tampilan menggunakan templat rute yang mirip default
dengan rute. REST API harus menggunakan perutean atribut.
Templat "{controller=Home}/{action=Index}/{id?}"
rute :
Cocok dengan jalur URL seperti
/Products/Details/5
Mengekstrak nilai
{ controller = Products, action = Details, id = 5 }
rute dengan membuat token jalur. Ekstraksi nilai rute menghasilkan kecocokan jika aplikasi memiliki pengontrol bernamaProductsController
danDetails
tindakan:public class ProductsController : Controller { public IActionResult Details(int id) { return ControllerContext.MyDisplayRouteInfo(id); } }
MyDisplayRouteInfo disediakan oleh paket Rick.Docs.Samples.RouteInfo NuGet dan menampilkan informasi rute.
/Products/Details/5
model mengikat nilaiid = 5
untuk mengatur parameter keid
5
. Lihat Pengikatan Model untuk detail selengkapnya.{controller=Home}
Home
mendefinisikan sebagai defaultcontroller
.{action=Index}
Index
mendefinisikan sebagai defaultaction
.Karakter
?
dalam{id?}
mendefinisikanid
sebagai opsional.Parameter rute default dan opsional tidak perlu ada di jalur URL untuk kecocokan. Lihat Referensi Templat Rute untuk deskripsi terperinci tentang sintaks templat rute.
Cocok dengan jalur
/
URL .Menghasilkan nilai
{ controller = Home, action = Index }
rute .
Nilai untuk controller
dan action
memanfaatkan nilai default. id
tidak menghasilkan nilai karena tidak ada segmen yang sesuai di jalur URL. /
hanya cocok jika ada HomeController
tindakan dan Index
:
public class HomeController : Controller
{
public IActionResult Index() { ... }
}
Menggunakan definisi pengontrol sebelumnya dan templat rute, HomeController.Index
tindakan dijalankan untuk jalur URL berikut:
/Home/Index/17
/Home/Index
/Home
/
Jalur /
URL menggunakan pengontrol dan Index
tindakan default Home
templat rute. Jalur /Home
URL menggunakan tindakan default Index
templat rute.
Metode MapDefaultControllerRoutekenyamanan :
endpoints.MapDefaultControllerRoute();
Menggantikan:
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
Penting
Perutean dikonfigurasi menggunakan UseRoutingmiddleware , MapControllerRoute
, dan MapAreaControllerRoute
. Untuk menggunakan pengontrol:
- Panggil MapControllers di dalam
UseEndpoints
untuk memetakan pengontrol yang dirutekan atribut. - Panggil MapControllerRoute atau MapAreaControllerRoute, untuk memetakan pengontrol yang dirutekan secara konvensional dan atribut pengontrol yang dirutekan .
Perutean konvensional
Perutean konvensional digunakan dengan pengontrol dan tampilan. Rute default
:
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Sebelumnya adalah contoh rute konvensional. Ini disebut perutean konvensional karena menetapkan konvensi untuk jalur URL:
- Segmen jalur pertama,
{controller=Home}
, memetakan ke nama pengontrol. - Segmen kedua,
{action=Index}
, memetakan ke nama tindakan . - Segmen ketiga,
{id?}
digunakan untuk opsionalid
. in?
{id?}
membuatnya opsional.id
digunakan untuk memetakan ke entitas model.
Dengan menggunakan rute ini default
, jalur URL:
/Products/List
memetakan keProductsController.List
tindakan./Blog/Article/17
memetakan keBlogController.Article
dan biasanya model mengikatid
parameter ke 17.
Pemetaan ini:
- Hanya didasarkan pada pengontrol dan nama tindakan.
- Tidak didasarkan pada namespace, lokasi file sumber, atau parameter metode.
Menggunakan perutean konvensional dengan rute default memungkinkan pembuatan aplikasi tanpa harus membuat pola URL baru untuk setiap tindakan. Untuk aplikasi dengan tindakan gaya CRUD , memiliki konsistensi untuk URL di seluruh pengontrol:
- Membantu menyederhanakan kode.
- Membuat UI lebih mudah diprediksi.
Peringatan
id
dalam kode sebelumnya didefinisikan sebagai opsional oleh templat rute. Tindakan dapat dijalankan tanpa ID opsional yang disediakan sebagai bagian dari URL. Umumnya, kapan id
dihilangkan dari URL:
id
diatur ke0
berdasarkan pengikatan model.- Tidak ada entitas yang ditemukan dalam database yang cocok
id == 0
.
Perutean atribut memberikan kontrol menenangkan untuk membuat ID diperlukan untuk beberapa tindakan dan bukan untuk yang lain. Menurut konvensi, dokumentasi menyertakan parameter opsional seperti id
saat kemungkinan akan muncul dalam penggunaan yang benar.
Sebagian besar aplikasi harus memilih skema perutean dasar dan deskriptif sehingga URL dapat dibaca dan bermakna. Rute {controller=Home}/{action=Index}/{id?}
konvensional default :
- Mendukung skema perutean dasar dan deskriptif.
- Adalah titik awal yang berguna untuk aplikasi berbasis UI.
- Adalah satu-satunya templat rute yang diperlukan untuk banyak aplikasi UI web. Untuk aplikasi UI web yang lebih besar, rute lain yang menggunakan Area sering kali merupakan semua yang diperlukan.
MapControllerRoute dan MapAreaRoute :
- Secara otomatis menetapkan nilai pesanan ke titik akhir mereka berdasarkan urutan yang dipanggil.
Perutean titik akhir di ASP.NET Core 3.0 dan yang lebih baru:
- Tidak memiliki konsep rute.
- Tidak memberikan jaminan pemesanan untuk eksekusi ekstensibilitas, semua titik akhir diproses sekaligus.
Aktifkan Pengelogan untuk melihat bagaimana implementasi perutean bawaan, seperti Route, mencocokkan permintaan.
Perutean atribut dijelaskan nanti dalam dokumen ini.
Beberapa rute konvensional
Beberapa rute konvensional dapat ditambahkan ke dalam UseEndpoints
dengan menambahkan lebih banyak panggilan ke MapControllerRoute dan MapAreaControllerRoute. Melakukannya memungkinkan menentukan beberapa konvensi, atau menambahkan rute konvensional yang didedikasikan untuk tindakan tertentu, seperti:
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?}");
});
Rute blog
dalam kode sebelumnya adalah rute konvensional khusus. Ini disebut rute konvensional khusus karena:
Karena controller
dan action
tidak muncul di templat "blog/{*article}"
rute sebagai parameter:
- Mereka hanya dapat memiliki nilai
{ controller = "Blog", action = "Article" }
default . - Rute ini selalu memetakan ke tindakan
BlogController.Article
.
/Blog
, /Blog/Article
, dan /Blog/{any-string}
adalah satu-satunya jalur URL yang cocok dengan rute blog.
Contoh sebelumnya:
blog
rute memiliki prioritas yang lebih tinggi untuk kecocokandefault
daripada rute karena ditambahkan terlebih dahulu.- Adalah contoh perutean gaya Slug di mana biasanya memiliki nama artikel sebagai bagian dari URL.
Peringatan
Di ASP.NET Core 3.0 dan yang lebih baru, perutean tidak:
- Tentukan konsep yang disebut rute.
UseRouting
menambahkan pencocokan rute ke alur middleware. MiddlewareUseRouting
melihat kumpulan titik akhir yang ditentukan dalam aplikasi, dan memilih kecocokan titik akhir terbaik berdasarkan permintaan. - Berikan jaminan tentang urutan eksekusi ekstensibilitas seperti IRouteConstraint atau IActionConstraint.
Lihat Perutean untuk bahan referensi tentang perutean.
Urutan perutean konvensional
Perutean konvensional hanya cocok dengan kombinasi tindakan dan pengontrol yang ditentukan oleh aplikasi. Ini dimaksudkan untuk menyederhanakan kasus di mana rute konvensional tumpang tindih.
Menambahkan rute menggunakan MapControllerRoute, MapDefaultControllerRoute, dan MapAreaControllerRoute secara otomatis menetapkan nilai pesanan ke titik akhir mereka berdasarkan urutan yang dipanggil. Kecocokan dari rute yang muncul sebelumnya memiliki prioritas yang lebih tinggi. Perutean konvensional bergantung pada pesanan. Secara umum, rute dengan area harus ditempatkan lebih awal karena lebih spesifik daripada rute tanpa area. Rute konvensional khusus dengan parameter rute catch-all seperti {*article}
dapat membuat rute terlalu serakah, yang berarti bahwa rute tersebut cocok dengan URL yang Anda maksudkan untuk dicocokkan dengan rute lain. Letakkan rute serakah nanti di tabel rute untuk mencegah kecocokan serakah.
Peringatan
Parameter catch-all mungkin salah mencocokkan rute karena bug dalam perutean. Aplikasi yang terpengaruh oleh bug ini memiliki karakteristik berikut:
- Rute catch-all, misalnya,
{**slug}"
- Rute catch-all gagal mencocokkan permintaan yang harus cocok.
- Menghapus rute lain membuat rute catch-all mulai berfungsi.
Lihat bug GitHub 18677 dan 16579 misalnya kasus yang mengenai bug ini.
Perbaikan keikutsertaan untuk bug ini terkandung dalam .NET Core 3.1.301 SDK dan yang lebih baru. Kode berikut menetapkan sakelar internal yang memperbaiki bug ini:
public static void Main(string[] args)
{
AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior",
true);
CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.
Mengatasi tindakan ambigu
Saat dua titik akhir cocok melalui perutean, perutean harus melakukan salah satu hal berikut:
- Pilih kandidat terbaik.
- Tampilkan pengecualian.
Contohnya:
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);
}
}
}
Pengontrol sebelumnya mendefinisikan dua tindakan yang cocok:
- Jalur URL
/Products33/Edit/17
- Merutekan data
{ controller = Products33, action = Edit, id = 17 }
.
Ini adalah pola umum untuk pengontrol MVC:
Edit(int)
menampilkan formulir untuk mengedit produk.Edit(int, Product)
memproses formulir yang diposting.
Untuk mengatasi rute yang benar:
Edit(int, Product)
dipilih ketika permintaan adalah HTTPPOST
.Edit(int)
dipilih ketika kata kerja HTTP adalah hal lain.Edit(int)
umumnya dipanggil melaluiGET
.
HttpPostAttribute, [HttpPost]
, disediakan untuk perutean sehingga dapat memilih berdasarkan metode HTTP permintaan. Edit(int, Product)
membuat HttpPostAttribute
kecocokan yang lebih baik daripada Edit(int)
.
Penting untuk memahami peran atribut seperti HttpPostAttribute
. Atribut serupa didefinisikan untuk kata kerja HTTP lainnya. Dalam perutean konvensional, umum bagi tindakan untuk menggunakan nama tindakan yang sama saat mereka menjadi bagian dari formulir acara, kirim alur kerja formulir. Misalnya, lihat Memeriksa dua metode tindakan Edit.
Jika perutean tidak dapat memilih kandidat terbaik, sebuah AmbiguousMatchException dilemparkan, mencantumkan beberapa titik akhir yang cocok.
Nama rute konvensional
String "blog"
dan "default"
dalam contoh berikut adalah nama rute konvensional:
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?}");
});
Nama rute memberi rute nama logis. Rute bernama dapat digunakan untuk pembuatan URL. Menggunakan rute bernama menyederhanakan pembuatan URL saat pengurutan rute dapat membuat pembuatan URL menjadi rumit. Nama rute harus lebar aplikasi yang unik.
Nama rute:
- Tidak berdampak pada pencocokan URL atau penanganan permintaan.
- Hanya digunakan untuk pembuatan URL.
Konsep nama rute diwakili dalam perutean sebagai IEndpointNameMetadata. Nama rute istilah dan nama titik akhir:
- Bisa dipertukarkan.
- Mana yang digunakan dalam dokumentasi dan kode tergantung pada API yang dijelaskan.
Perutean atribut untuk REST API
REST API harus menggunakan perutean atribut untuk memodelkan fungsionalitas aplikasi sebagai sekumpulan sumber daya di mana operasi diwakili oleh kata kerja HTTP.
Perutean atribut menggunakan sekumpulan atribut untuk memetakan tindakan langsung ke templat rute. Kode berikut StartUp.Configure
khas untuk REST API dan digunakan dalam sampel berikutnya:
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();
});
}
Dalam kode sebelumnya, MapControllers dipanggil ke dalam UseEndpoints
untuk memetakan pengontrol rute atribut.
Dalam contoh berikut:
HomeController
cocok dengan sekumpulan URL yang mirip dengan apa yang cocok dengan rute{controller=Home}/{action=Index}/{id?}
konvensional default.
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);
}
}
Tindakan HomeController.Index
dijalankan untuk salah satu jalur /
URL , , /Home
, /Home/Index
atau /Home/Index/3
.
Contoh ini menyoroti perbedaan pemrograman utama antara perutean atribut dan perutean konvensional. Perutean atribut memerlukan lebih banyak input untuk menentukan rute. Rute default konvensional menangani rute dengan lebih tepat. Namun, perutean atribut memungkinkan dan memerlukan kontrol yang tepat tentang templat rute mana yang berlaku untuk setiap tindakan.
Dengan perutean atribut, pengontrol dan nama tindakan tidak memainkan bagian di mana tindakan dicocokkan, kecuali penggantian token digunakan. Contoh berikut cocok dengan URL yang sama dengan contoh sebelumnya:
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);
}
}
Kode berikut menggunakan penggantian token untuk action
dan controller
:
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();
}
}
Kode berikut berlaku [Route("[controller]/[action]")]
untuk pengontrol:
[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();
}
}
Dalam kode sebelumnya, Index
templat metode harus didahului /
atau ~/
ke templat rute. Templat rute diterapkan ke tindakan yang dimulai dengan /
atau ~/
tidak digabungkan dengan templat rute yang diterapkan ke pengontrol.
Lihat Prioritas templat rute untuk informasi tentang pemilihan templat rute.
Nama perutean yang dicadangkan
Kata kunci berikut adalah nama parameter rute yang dipesan saat menggunakan Pengontrol atau Razor Halaman:
action
area
controller
handler
page
Menggunakan page
sebagai parameter rute dengan perutean atribut adalah kesalahan umum. Melakukan itu menghasilkan perilaku yang tidak konsisten dan membingungkan dengan pembuatan URL.
public class MyDemo2Controller : Controller
{
[Route("/articles/{page}")]
public IActionResult ListArticles(int page)
{
return ControllerContext.MyDisplayRouteInfo(page);
}
}
Nama parameter khusus digunakan oleh pembuatan URL untuk menentukan apakah operasi pembuatan URL mengacu pada Razor Halaman atau ke Pengontrol.
Kata kunci berikut dicadangkan dalam konteks Razor tampilan atau Razor Halaman:
page
using
namespace
inject
section
inherits
model
addTagHelper
removeTagHelper
Kata kunci ini tidak boleh digunakan untuk pembuatan tautan, parameter terikat model, atau properti tingkat atas.
Templat kata kerja HTTP
ASP.NET Core memiliki templat kata kerja HTTP berikut:
Templat rute
ASP.NET Core memiliki templat rute berikut:
- Semua templat kata kerja HTTP adalah templat rute.
- [Rute]
Perutean atribut dengan atribut kata kerja Http
Pertimbangkan pengontrol berikut:
[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);
}
}
Dalam kode sebelumnya:
- Setiap tindakan berisi
[HttpGet]
atribut , yang membatasi pencocokan dengan permintaan HTTP GET saja. - Tindakan ini
GetProduct
mencakup"{id}"
templat, oleh karena ituid
ditambahkan ke"api/[controller]"
templat pada pengontrol. Templat metodenya adalah"api/[controller]/{id}"
. Oleh karena itu tindakan ini hanya cocok dengan permintaan GET untuk formulir/api/test2/xyz
,/api/test2/123
,/api/test2/{any string}
, dll.[HttpGet("{id}")] // GET /api/test2/xyz public IActionResult GetProduct(string id) { return ControllerContext.MyDisplayRouteInfo(id); }
- Tindakan
GetIntProduct
berisi"int/{id:int}"
templat. Bagian:int
templat membatasiid
nilai rute ke string yang dapat dikonversi menjadi bilangan bulat. Permintaan GET ke/api/test2/int/abc
:- Tidak cocok dengan tindakan ini.
- Mengembalikan kesalahan 404 Tidak Ditemukan .
[HttpGet("int/{id:int}")] // GET /api/test2/int/3 public IActionResult GetIntProduct(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
- Tindakan
GetInt2Product
berisi{id}
dalam templat, tetapi tidak membatasiid
nilai yang dapat dikonversi menjadi bilangan bulat. Permintaan GET ke/api/test2/int2/abc
:- Cocok dengan rute ini.
- Pengikatan model gagal dikonversi
abc
ke bilangan bulat. Parameterid
metode adalah bilangan bulat. - Mengembalikan Permintaan Buruk 400 karena pengikatan model gagal dikonversi
abc
ke bilangan bulat.[HttpGet("int2/{id}")] // GET /api/test2/int2/3 public IActionResult GetInt2Product(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
Perutean atribut dapat menggunakan HttpMethodAttribute atribut seperti HttpPostAttribute, , HttpPutAttributedan HttpDeleteAttribute. Semua atribut kata kerja HTTP menerima templat rute. Contoh berikut menunjukkan dua tindakan yang cocok dengan templat rute yang sama:
[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);
}
}
Menggunakan jalur /products3
URL :
- Tindakan
MyProductsController.ListProducts
berjalan ketika kata kerja HTTP adalahGET
. - Tindakan
MyProductsController.CreateProduct
berjalan ketika kata kerja HTTP adalahPOST
.
Saat membangun REST API, jarang anda harus menggunakan [Route(...)]
pada metode tindakan karena tindakan menerima semua metode HTTP. Lebih baik menggunakan atribut kata kerja HTTP yang lebih spesifik agar tepat tentang apa yang didukung API Anda. REST Klien API diharapkan mengetahui jalur dan kata kerja HTTP apa yang dipetakan ke operasi logis tertentu.
REST API harus menggunakan perutean atribut untuk memodelkan fungsionalitas aplikasi sebagai sekumpulan sumber daya di mana operasi diwakili oleh kata kerja HTTP. Ini berarti bahwa banyak operasi, misalnya, GET dan POST pada sumber daya logis yang sama menggunakan URL yang sama. Perutean atribut menyediakan tingkat kontrol yang diperlukan untuk merancang tata letak titik akhir publik API dengan hati-hati.
Karena rute atribut berlaku untuk tindakan tertentu, mudah untuk membuat parameter yang diperlukan sebagai bagian dari definisi templat rute. Dalam contoh berikut, id
diperlukan sebagai bagian dari jalur URL:
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Tindakan Products2ApiController.GetProduct(int)
:
- Dijalankan dengan jalur URL seperti
/products2/3
- Tidak dijalankan dengan jalur
/products2
URL .
Dengan atribut [Consumes], suatu tindakan dapat membatasi jenis konten permintaan yang didukung. Untuk informasi selengkapnya, lihat Menentukan jenis konten permintaan yang didukung dengan atribut Konsumsi.
Lihat Perutean untuk deskripsi lengkap templat rute dan opsi terkait.
Untuk informasi selengkapnya tentang [ApiController]
, lihat atribut ApiController.
Nama rute
Kode berikut mendefinisikan nama rute :Products_List
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Nama rute dapat digunakan untuk menghasilkan URL berdasarkan rute tertentu. Nama rute:
- Tidak berdampak pada perilaku perutean pencocokan URL.
- Hanya digunakan untuk pembuatan URL.
Nama rute harus unik di seluruh aplikasi.
Berbeda dengan kode sebelumnya dengan rute default konvensional, yang mendefinisikan id
parameter sebagai opsional ({id?}
). Kemampuan untuk menentukan API dengan tepat memiliki keuntungan, seperti mengizinkan /products
dan /products/5
dikirim ke tindakan yang berbeda.
Menggabungkan rute atribut
Untuk membuat perutean atribut kurang berulang, atribut rute pada pengontrol dikombinasikan dengan atribut rute pada tindakan individual. Templat rute apa pun yang ditentukan pada pengontrol telah ditambahkan ke templat rute pada tindakan. Menempatkan atribut rute pada pengontrol membuat semua tindakan di pengontrol menggunakan perutean atribut.
[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);
}
}
Dalam contoh sebelumnya:
- Jalur
/products
URL dapat cocokProductsApi.ListProducts
- Jalur
/products/5
URL dapat cocokProductsApi.GetProduct(int)
dengan .
Kedua tindakan ini hanya cocok dengan HTTP GET
karena ditandai dengan [HttpGet]
atribut .
Templat rute diterapkan ke tindakan yang dimulai dengan /
atau ~/
tidak digabungkan dengan templat rute yang diterapkan ke pengontrol. Contoh berikut cocok dengan sekumpulan jalur URL yang mirip dengan rute default.
[Route("Home")]
public class HomeController : Controller
{
[Route("")]
[Route("Index")]
[Route("/")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
[Route("About")]
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Tabel berikut menjelaskan [Route]
atribut dalam kode sebelumnya:
Atribut | Menggabungkan dengan [Route("Home")] |
Menentukan templat rute |
---|---|---|
[Route("")] |
Ya | "Home" |
[Route("Index")] |
Ya | "Home/Index" |
[Route("/")] |
Tidak | "" |
[Route("About")] |
Ya | "Home/About" |
Urutan rute atribut
Perutean membangun pohon dan mencocokkan semua titik akhir secara bersamaan:
- Entri rute berulah seolah-olah ditempatkan dalam urutan yang ideal.
- Rute yang paling spesifik memiliki kesempatan untuk dijalankan sebelum rute yang lebih umum.
Misalnya, rute atribut seperti blog/search/{topic}
lebih spesifik daripada rute atribut seperti blog/{*article}
. Rute blog/search/{topic}
memiliki prioritas yang lebih tinggi, secara default, karena lebih spesifik. Dengan menggunakan perutean konvensional, pengembang bertanggung jawab untuk menempatkan rute dalam urutan yang diinginkan.
Rute atribut dapat mengonfigurasi pesanan Order menggunakan properti . Semua atribut rute yang disediakan kerangka kerja meliputi Order
. Rute diproses sesuai dengan jenis properti yang Order
naik. Urutan defaultnya adalah 0
. Mengatur rute menggunakan Order = -1
eksekusi sebelum rute yang tidak mengatur pesanan. Mengatur rute menggunakan Order = 1
eksekusi setelah pemesanan rute default.
Hindari tergantung pada Order
. Jika ruang URL aplikasi memerlukan nilai pesanan eksplisit untuk merutekan dengan benar, kemungkinan membingungkan klien juga. Secara umum, perutean atribut memilih rute yang benar dengan pencocokan URL. Jika urutan default yang digunakan untuk pembuatan URL tidak berfungsi, menggunakan nama rute sebagai penimpaan biasanya lebih sederhana daripada menerapkan Order
properti.
Pertimbangkan dua pengontrol berikut yang keduanya menentukan pencocokan /home
rute :
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);
}
}
/home
Meminta dengan kode sebelumnya melemparkan pengecualian yang mirip dengan yang berikut ini:
AmbiguousMatchException: The request matched multiple endpoints. Matches:
WebMvcRouting.Controllers.HomeController.Index
WebMvcRouting.Controllers.MyDemoController.MyIndex
Order
Menambahkan ke salah satu atribut rute menyelesaikan ambiguitas:
[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
return ControllerContext.MyDisplayRouteInfo();
}
Dengan kode sebelumnya, /home
menjalankan HomeController.Index
titik akhir. Untuk sampai ke MyDemoController.MyIndex
permintaan , /home/MyIndex
. Catatan:
- Kode sebelumnya adalah contoh atau desain perutean yang buruk. Ini digunakan untuk mengilustrasikan
Order
properti . - Properti
Order
hanya menyelesaikan ambiguitas, templat tersebut tidak dapat dicocokkan. Akan lebih baik untuk menghapus[Route("Home")]
templat.
Lihat Razor Rute halaman dan konvensi aplikasi: Urutan rute untuk informasi tentang urutan rute dengan Razor Halaman.
Dalam beberapa kasus, kesalahan HTTP 500 dikembalikan dengan rute ambigu. Gunakan pengelogan untuk melihat titik akhir mana yang menyebabkan AmbiguousMatchException
.
Penggantian token dalam templat rute [pengontrol], [tindakan], [area]
Untuk kenyamanan, rute atribut mendukung penggantian token dengan menyertakan token dalam kurung siku ([
, ]
). Token [action]
, [area]
, dan [controller]
diganti dengan nilai nama tindakan, nama area, dan nama pengontrol dari tindakan tempat rute ditentukan:
[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);
}
}
Dalam kode sebelumnya:
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
- Korek api
/Products0/List
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
- Korek api
/Products0/Edit/{id}
Penggantian token terjadi sebagai langkah terakhir membangun rute atribut. Contoh sebelumnya berpura-pura sama dengan kode berikut:
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);
}
}
Jika Anda membaca ini dalam bahasa selain bahasa Inggris, beri tahu kami dalam masalah diskusi GitHub ini jika Anda ingin melihat komentar kode dalam bahasa asli Anda.
Rute atribut juga dapat dikombinasikan dengan warisan. Ini kuat dikombinasikan dengan penggantian token. Penggantian token juga berlaku untuk nama rute yang ditentukan oleh rute atribut.
[Route("[controller]/[action]", Name="[controller]_[action]")]
menghasilkan nama rute unik untuk setiap tindakan:
[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);
}
}
Untuk mencocokkan pemisah [
penggantian token harfiah atau ]
, keluarkan dengan mengulangi karakter ([[
atau ]]
).
Menggunakan transformator parameter untuk menyesuaikan penggantian token
Penggantian token dapat disesuaikan menggunakan transformator parameter. Transformator parameter mengimplementasikan IOutboundParameterTransformer dan mengubah nilai parameter. Misalnya, transformator parameter kustom SlugifyParameterTransformer
mengubah SubscriptionManagement
nilai rute menjadi subscription-management
:
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();
}
}
RouteTokenTransformerConvention adalah konvensi model aplikasi yang:
- Menerapkan transformator parameter ke semua rute atribut dalam aplikasi.
- Menyesuaikan nilai token rute atribut saat diganti.
public class SubscriptionManagementController : Controller
{
[HttpGet("[controller]/[action]")]
public IActionResult ListAll()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Metode sebelumnya ListAll
cocok /subscription-management/list-all
dengan .
RouteTokenTransformerConvention
terdaftar sebagai opsi di ConfigureServices
.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Conventions.Add(new RouteTokenTransformerConvention(
new SlugifyParameterTransformer()));
});
}
Lihat dokumen web MDN di Slug untuk definisi Slug.
Peringatan
Saat menggunakan System.Text.RegularExpressions untuk memproses masukan yang tidak tepercaya, berikan batas waktu. Pengguna jahat dapat memberikan masukan ke RegularExpressions
yang menyebabkan Penolakan Serangan Layanan. ASP.NET API kerangka kerja Core yang menggunakan RegularExpressions
batas waktu.
Beberapa rute atribut
Perutean atribut mendukung penentuan beberapa rute yang mencapai tindakan yang sama. Penggunaan yang paling umum adalah meniru perilaku rute konvensional default seperti yang ditunjukkan dalam contoh berikut:
[Route("[controller]")]
public class Products13Controller : Controller
{
[Route("")] // Matches 'Products13'
[Route("Index")] // Matches 'Products13/Index'
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
Menempatkan beberapa atribut rute pada pengontrol berarti masing-masing menggabungkan dengan masing-masing atribut rute pada metode tindakan:
[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();
}
}
Semua batasan rute kata kerja HTTP menerapkan IActionConstraint
.
Saat beberapa atribut rute yang diterapkan IActionConstraint ditempatkan pada tindakan:
- Setiap batasan tindakan dikombinasikan dengan templat rute yang diterapkan ke pengontrol.
[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();
}
}
Menggunakan beberapa rute pada tindakan mungkin tampak berguna dan kuat, lebih baik menjaga ruang URL aplikasi Anda tetap dasar dan terdefinisi dengan baik. Gunakan beberapa rute pada tindakan hanya jika diperlukan, misalnya, untuk mendukung klien yang ada.
Menentukan parameter opsional rute atribut, nilai default, dan batasan
Rute atribut mendukung sintaksis sebaris yang sama dengan rute konvensional untuk menentukan parameter opsional, nilai default, dan batasan.
public class Products14Controller : Controller
{
[HttpPost("product14/{id:int}")]
public IActionResult ShowProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Dalam kode sebelumnya, [HttpPost("product14/{id:int}")]
menerapkan batasan rute. Tindakan Products14Controller.ShowProduct
hanya dicocokkan dengan jalur URL seperti /product14/3
. Bagian {id:int}
templat rute membatasi segmen tersebut hanya untuk bilangan bulat.
Lihat Referensi Templat Rute untuk deskripsi terperinci tentang sintaks templat rute.
Atribut rute kustom menggunakan IRouteTemplateProvider
Semua atribut rute menerapkan IRouteTemplateProvider. Runtime ASP.NET Core:
- Mencari atribut pada kelas pengontrol dan metode tindakan saat aplikasi dimulai.
- Menggunakan atribut yang diterapkan
IRouteTemplateProvider
untuk membangun serangkaian rute awal.
Terapkan IRouteTemplateProvider
untuk menentukan atribut rute kustom. Masing-masing IRouteTemplateProvider
memungkinkan Anda menentukan satu rute dengan templat, pesanan, dan nama rute kustom:
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();
}
}
Metode sebelumnya Get
mengembalikan Order = 2, Template = api/MyTestApi
.
Menggunakan model aplikasi untuk menyesuaikan rute atribut
Model aplikasi:
- Adalah model objek yang dibuat saat startup.
- Berisi semua metadata yang digunakan oleh ASP.NET Core untuk merutekan dan menjalankan tindakan dalam aplikasi.
Model aplikasi mencakup semua data yang dikumpulkan dari atribut rute. Data dari atribut rute disediakan oleh IRouteTemplateProvider
implementasi. Konvensi:
- Dapat ditulis untuk mengubah model aplikasi untuk menyesuaikan perilaku perutean.
- Dibaca saat pengaktifan aplikasi.
Bagian ini menunjukkan contoh dasar penyesuaian perutean menggunakan model aplikasi. Kode berikut membuat rute kira-kira sejajar dengan struktur folder proyek.
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()
};
}
}
}
Kode berikut mencegah namespace
konvensi diterapkan ke pengontrol yang dirutekan atribut:
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
Misalnya, pengontrol berikut tidak menggunakan NamespaceRoutingConvention
:
[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}");
}
}
Metode NamespaceRoutingConvention.Apply
:
- Tidak melakukan apa pun jika pengontrol dirutekan atributnya.
- Mengatur templat pengontrol berdasarkan
namespace
, dengan basisnamespace
dihapus.
NamespaceRoutingConvention
dapat diterapkan dalam Startup.ConfigureServices
:
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.
Misalnya, pertimbangkan pengontrol berikut:
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}");
}
}
}
Dalam kode sebelumnya:
- Basisnya
namespace
adalahMy.Application
. - Nama lengkap pengontrol sebelumnya adalah
My.Application.Admin.Controllers.UsersController
. - Mengatur
NamespaceRoutingConvention
templat pengontrol keAdmin/Controllers/Users/[action]/{id?
.
NamespaceRoutingConvention
juga dapat diterapkan sebagai atribut pada pengontrol:
[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}");
}
}
Perutean campuran: Perutean atribut vs perutean konvensional
aplikasi ASP.NET Core dapat mencampur penggunaan perutean konvensional dan perutean atribut. Biasanya menggunakan rute konvensional untuk pengontrol yang melayani halaman HTML untuk browser, dan perutean atribut untuk pengontrol yang melayani REST API.
Tindakan dirutekan secara konvensional atau atribut yang dirutekan. Menempatkan rute pada pengontrol atau tindakan membuatnya atribut dirutekan. Tindakan yang menentukan rute atribut tidak dapat dicapai melalui rute konvensional dan sebaliknya. Atribut rute apa pun pada pengontrol membuat semua tindakan dalam atribut pengontrol dirutekan.
Perutean atribut dan perutean konvensional menggunakan mesin perutean yang sama.
Pembuatan URL dan nilai sekitar
Aplikasi dapat menggunakan fitur pembuatan URL perutean untuk menghasilkan tautan URL ke tindakan. Menghasilkan URL menghilangkan URL hardcoding, membuat kode lebih kuat dan dapat dipertahankan. Bagian ini berfokus pada fitur pembuatan URL yang disediakan oleh MVC dan hanya mencakup dasar-dasar cara kerja pembuatan URL. Lihat Perutean untuk deskripsi terperinci tentang pembuatan URL.
Antarmuka IUrlHelper adalah elemen infrastruktur yang mendasar antara MVC dan perutean untuk pembuatan URL. Instans IUrlHelper
tersedia melalui Url
properti di pengontrol, tampilan, dan komponen tampilan.
Dalam contoh berikut, IUrlHelper
antarmuka digunakan melalui Controller.Url
properti untuk menghasilkan URL ke tindakan lain.
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();
}
}
Jika aplikasi menggunakan rute konvensional default, nilai url
variabel adalah string /UrlGeneration/Destination
jalur URL . Jalur URL ini dibuat dengan perutean dengan menggabungkan:
- Nilai rute dari permintaan saat ini, yang disebut nilai sekitar.
- Nilai yang diteruskan ke
Url.Action
dan mengganti nilai tersebut ke dalam templat rute:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}
result: /UrlGeneration/Destination
Setiap parameter rute dalam templat rute memiliki nilainya digantikan dengan mencocokkan nama dengan nilai dan nilai sekitar. Parameter rute yang tidak memiliki nilai dapat:
- Gunakan nilai default jika memilikinya.
- Dilewati jika bersifat opsional. Misalnya,
id
dari templat{controller}/{action}/{id?}
rute .
Pembuatan URL gagal jika parameter rute yang diperlukan tidak memiliki nilai yang sesuai. Jika pembuatan URL gagal untuk rute, rute berikutnya dicoba hingga semua rute telah dicoba atau kecocokan ditemukan.
Contoh Url.Action
sebelumnya mengasumsikan perutean konvensional. Pembuatan URL berfungsi sama dengan perutean atribut, meskipun konsepnya berbeda. Dengan perutean konvensional:
- Nilai rute digunakan untuk memperluas templat.
- Nilai rute untuk
controller
danaction
biasanya muncul dalam templat tersebut. Ini berfungsi karena URL yang cocok dengan perutean mematuhi konvensi.
Contoh berikut menggunakan perutean atribut:
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();
}
}
Tindakan Source
dalam kode sebelumnya menghasilkan custom/url/to/destination
.
LinkGenerator ditambahkan di ASP.NET Core 3.0 sebagai alternatif untuk IUrlHelper
. LinkGenerator
menawarkan fungsionalitas yang serupa tetapi lebih fleksibel. Setiap metode pada IUrlHelper
memiliki keluarga metode yang LinkGenerator
sesuai juga.
Membuat URL menurut nama tindakan
Url.Action, LinkGenerator.GetPathByAction, dan semua kelebihan beban terkait semuanya dirancang untuk menghasilkan titik akhir target dengan menentukan nama pengontrol dan nama tindakan.
Saat menggunakan Url.Action
, nilai rute saat ini untuk controller
dan action
disediakan oleh runtime:
- Nilai
controller
danaction
merupakan bagian dari nilai dan nilai sekitar. MetodeUrl.Action
ini selalu menggunakan nilaiaction
saat ini dan dancontroller
menghasilkan jalur URL yang dirutekan ke tindakan saat ini.
Perutean mencoba menggunakan nilai dalam nilai sekitar untuk mengisi informasi yang tidak disediakan saat membuat URL. Pertimbangkan rute seperti {a}/{b}/{c}/{d}
dengan nilai { a = Alice, b = Bob, c = Carol, d = David }
sekitar :
- Perutean memiliki informasi yang cukup untuk menghasilkan URL tanpa nilai tambahan.
- Perutean memiliki informasi yang cukup karena semua parameter rute memiliki nilai.
Jika nilai { d = Donovan }
ditambahkan:
- Nilai
{ d = David }
diabaikan. - Jalur URL yang dihasilkan adalah
Alice/Bob/Carol/Donovan
.
Peringatan: Jalur URL bersifat hierarkis. Dalam contoh sebelumnya, jika nilai { c = Cheryl }
ditambahkan:
- Kedua nilai
{ c = Carol, d = David }
diabaikan. - Tidak ada lagi nilai untuk
d
pembuatan URL dan gagal. - Nilai yang diinginkan dan
c
d
harus ditentukan untuk menghasilkan URL.
Anda mungkin mengharapkan untuk mengalami masalah ini dengan rute {controller}/{action}/{id?}
default . Masalah ini jarang terjadi dalam praktiknya karena Url.Action
selalu secara eksplisit menentukan nilai controller
dan action
.
Beberapa kelebihan beban Url.Action mengambil objek nilai rute untuk menyediakan nilai untuk parameter rute selain controller
dan action
. Objek nilai rute sering digunakan dengan id
. Contohnya,Url.Action("Buy", "Products", new { id = 17 })
. Objek nilai rute:
- Menurut konvensi biasanya merupakan objek jenis anonim.
- Dapat berupa
IDictionary<>
atau POCO).
Nilai rute tambahan apa pun yang tidak cocok dengan parameter rute dimasukkan ke dalam string kueri.
public IActionResult Index()
{
var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
return Content(url);
}
Kode sebelumnya menghasilkan /Products/Buy/17?color=red
.
Kode berikut menghasilkan URL absolut:
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);
}
Untuk membuat URL absolut, gunakan salah satu hal berikut:
- Kelebihan beban yang menerima
protocol
. Misalnya, kode sebelumnya. - LinkGenerator.GetUriByAction, yang menghasilkan URI absolut secara default.
Hasilkan URL menurut rute
Kode sebelumnya menunjukkan pembuatan URL dengan meneruskan pengontrol dan nama tindakan. IUrlHelper
juga menyediakan keluarga metode Url.RouteUrl . Metode ini mirip dengan Url.Action, tetapi tidak menyalin nilai action
saat ini dan controller
ke nilai rute. Penggunaan yang paling umum dari Url.RouteUrl
:
- Menentukan nama rute untuk menghasilkan URL.
- Umumnya tidak menentukan pengontrol atau nama tindakan.
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();
}
File berikut Razor menghasilkan tautan HTML ke Destination_Route
:
<h1>Test Links</h1>
<ul>
<li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>
Hasilkan URL dalam HTML dan Razor
IHtmlHelperHtmlHelper menyediakan metode Html.BeginForm dan Html.ActionLink untuk menghasilkan <form>
elemen dan <a>
masing-masing. Metode ini menggunakan metode Url.Action untuk menghasilkan URL dan mereka menerima argumen serupa. Pendamping Url.RouteUrl
untuk HtmlHelper
adalah Html.BeginRouteForm
dan Html.RouteLink
yang memiliki fungsionalitas serupa.
TagHelpers menghasilkan URL melalui form
TagHelper dan <a>
TagHelper. Kedua penggunaan IUrlHelper
ini untuk implementasinya. Lihat Tag Helper dalam formulir untuk informasi selengkapnya.
Di dalam tampilan, IUrlHelper
tersedia melalui Url
properti untuk pembuatan URL ad-hoc apa pun yang tidak tercakup oleh yang di atas.
Pembuatan URL dalam Hasil Tindakan
Contoh sebelumnya yang diperlihatkan menggunakan IUrlHelper
dalam pengontrol. Penggunaan yang paling umum dalam pengontrol adalah menghasilkan URL sebagai bagian dari hasil tindakan.
Kelas ControllerBase dasar dan Controller menyediakan metode kenyamanan untuk hasil tindakan yang mereferensikan tindakan lain. Salah satu penggunaan umumnya adalah mengalihkan setelah menerima input pengguna:
[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);
}
Tindakan menghasilkan metode pabrik seperti RedirectToAction dan CreatedAtAction mengikuti pola yang mirip dengan metode pada IUrlHelper
.
Kasus khusus untuk rute konvensional khusus
Perutean konvensional dapat menggunakan jenis definisi rute khusus yang disebut rute konvensional khusus. Dalam contoh berikut, rute bernama blog
adalah rute konvensional khusus:
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?}");
});
Menggunakan definisi rute sebelumnya, Url.Action("Index", "Home")
menghasilkan jalur /
URL menggunakan default
rute, tetapi mengapa? Anda mungkin menebak nilai { controller = Home, action = Index }
rute akan cukup untuk menghasilkan URL menggunakan blog
, dan hasilnya adalah /blog?action=Index&controller=Home
.
Rute konvensional khusus mengandalkan perilaku khusus nilai default yang tidak memiliki parameter rute yang sesuai yang mencegah rute terlalu serakah dengan pembuatan URL. Dalam hal ini nilai defaultnya adalah { controller = Blog, action = Article }
, dan tidak controller
juga action
muncul sebagai parameter rute. Saat perutean melakukan pembuatan URL, nilai yang disediakan harus cocok dengan nilai default. Pembuatan URL menggunakan blog
gagal karena nilainya { controller = Home, action = Index }
tidak cocok { controller = Blog, action = Article }
. Perutean kemudian kembali untuk mencoba default
, yang berhasil.
Daerah
Area adalah fitur MVC yang digunakan untuk mengatur fungsionalitas terkait ke dalam grup sebagai terpisah:
- Perutean namespace layanan untuk tindakan pengontrol.
- Struktur folder untuk tampilan.
Menggunakan area memungkinkan aplikasi memiliki beberapa pengontrol dengan nama yang sama, selama memiliki area yang berbeda. Menggunakan area membuat hierarki untuk tujuan perutean dengan menambahkan parameter rute lain, area
ke controller
dan action
. Bagian ini membahas bagaimana perutean berinteraksi dengan area. Lihat Area untuk detail tentang bagaimana area digunakan dengan tampilan.
Contoh berikut mengonfigurasi MVC untuk menggunakan rute konvensional default dan area
rute untuk yang area
bernama Blog
:
app.UseEndpoints(endpoints =>
{
endpoints.MapAreaControllerRoute("blog_route", "Blog",
"Manage/{controller}/{action}/{id?}");
endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});
Dalam kode sebelumnya, MapAreaControllerRoute dipanggil untuk membuat "blog_route"
. Parameter kedua, "Blog"
, adalah nama area.
Saat mencocokkan jalur URL seperti /Manage/Users/AddUser
, "blog_route"
rute menghasilkan nilai { area = Blog, controller = Users, action = AddUser }
rute . Nilai area
rute dihasilkan oleh nilai default untuk area
. Rute yang dibuat oleh MapAreaControllerRoute
setara dengan yang berikut ini:
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?}");
});
MapAreaControllerRoute
membuat rute menggunakan nilai default dan batasan untuk area
menggunakan nama area yang disediakan, dalam hal Blog
ini . Nilai default memastikan bahwa rute selalu menghasilkan { area = Blog, ... }
, batasan memerlukan nilai { area = Blog, ... }
untuk pembuatan URL.
Perutean konvensional bergantung pada pesanan. Secara umum, rute dengan area harus ditempatkan lebih awal karena lebih spesifik daripada rute tanpa area.
Menggunakan contoh sebelumnya, nilai { area = Blog, controller = Users, action = AddUser }
rute cocok dengan tindakan berikut:
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}");
}
}
}
Atribut [Area] adalah yang menunjukkan pengontrol sebagai bagian dari area. Pengontrol ini ada di area tersebut Blog
. Pengontrol tanpa [Area]
atribut bukan anggota area apa pun, dan tidak cocok ketika area
nilai rute disediakan oleh perutean. Dalam contoh berikut, hanya pengontrol pertama yang tercantum yang dapat mencocokkan nilai { area = Blog, controller = Users, action = AddUser }
rute .
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}");
}
}
}
Namespace setiap pengontrol ditampilkan di sini untuk kelengkapan. Jika pengontrol sebelumnya menggunakan namespace yang sama, kesalahan kompilator akan dihasilkan. Namespace layanan kelas tidak berpengaruh pada perutean MVC.
Dua pengontrol pertama adalah anggota area, dan hanya cocok ketika nama area masing-masing disediakan oleh area
nilai rute. Pengontrol ketiga bukan anggota area apa pun, dan hanya dapat cocok ketika tidak ada nilai yang area
disediakan oleh perutean.
Dalam hal tidak ada nilai yang cocok, tidak adanya area
nilai sama seperti jika nilai untuk area
null atau string kosong.
Saat menjalankan tindakan di dalam area, nilai rute untuk area
tersedia sebagai nilai sekitar untuk perutean yang akan digunakan untuk pembuatan URL. Ini berarti bahwa secara default area bertindak lengket untuk pembuatan URL seperti yang ditunjukkan oleh sampel berikut.
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);
}
}
}
Kode berikut menghasilkan URL ke /Zebra/Users/AddUser
:
public class HomeController : Controller
{
public IActionResult About()
{
var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
return Content($"URL: {url}");
}
Definisi tindakan
Metode publik pada pengontrol, kecuali yang memiliki atribut NonAction , adalah tindakan.
Kode Sampel
- MyDisplayRouteInfo disediakan oleh paket Rick.Docs.Samples.RouteInfo NuGet dan menampilkan informasi rute.
- Melihat atau mengunduh kode sampel (cara mengunduh)
Men-debug diagnostik
Untuk output diagnostik perutean terperinci, atur Logging:LogLevel:Microsoft
ke Debug
. Di lingkungan pengembangan, atur tingkat log di appsettings.Development.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
ASP.NET Core