Pengarahan ke tindakan pengontrol di ASP.NET Core

Oleh Ryan Nowak, Kirk Larkin, dan Rick Anderson

Note

Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi artikel .NET 10 ini.

Warning

Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan Inti .NET dan .NET. Untuk rilis saat ini, lihat versi artikel .NET 10 ini.

ASP.NET Core menggunakan pengontrol Perutean middleware untuk mencocokkan URL permintaan masuk dan memetakannya ke aksi. 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.

Aksi dirutekan secara konvensional atau dengan 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 digunakan dengan REST API. Jika Anda terutama tertarik pada perutean untuk REST API, lanjutkan ke bagian Perutean Atribut untuk REST API.
    • Lihat Perutean untuk detail perutean tingkat lanjut.
  • Mengacu pada sistem routing default sebagai routing titik akhir. Anda dapat menggunakan pengontrol dengan versi perutean sebelumnya untuk tujuan kompatibilitas. Lihat panduan migrasi 2.2-3.0 untuk petunjuknya.

Menyiapkan rute konvensional

Templat MVC ASP.NET Core menghasilkan kode routing konvensional yang mirip dengan contoh berikut:

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();

Gunakan MapControllerRoute untuk membuat satu rute. Satu-satunya rute adalah rute default. Sebagian besar aplikasi dengan pengontrol dan tampilan menggunakan templat rute yang mirip dengan default 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 rute { controller = Products, action = Details, id = 5 } dengan men-tokenisasi jalur. Ekstraksi nilai rute menghasilkan kecocokan jika aplikasi memiliki pengontrol bernama ProductsController dan Details 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 nilai id = 5 untuk mengatur parameter ke id5. Untuk informasi selengkapnya, lihat Pengikatan Model.

  • {controller=Home} mendefinisikan Home sebagai controller default.

  • {action=Index} mendefinisikan Index sebagai action default.

  • Karakter ? dalam {id?} mendefinisikan id sebagai opsional.

    • Parameter rute default dan opsional tidak perlu ada di jalur URL untuk kecocokan. Untuk deskripsi terperinci tentang sintaks templat rute, lihat Referensi Templat Rute.
  • Cocok dengan jalur URL /.

  • Menghasilkan nilai rute { controller = Home, action = Index }.

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 tindakan HomeController dan Index.

public class HomeController : Controller
{
    public IActionResult Index() { ... }
}

Menggunakan definisi pengontrol sebelumnya dan templat rute, HomeController.Index tindakan berjalan untuk jalur URL berikut:

  • /Home/Index/17
  • /Home/Index
  • /Home
  • /

Jalur URL / menggunakan templat rute kontroler default Home dan aksi Index. Jalur URL /Home menggunakan tindakan default Index templat rute.

Metode kenyamanan MapDefaultControllerRoute:

app.MapDefaultControllerRoute();

Replaces:

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

Important

Perutean dikonfigurasi menggunakan middleware UseRouting dan UseEndpoints. Untuk menggunakan pengontrol:

Aplikasi biasanya tidak perlu memanggil UseRouting atau UseEndpoints. WebApplicationBuilder mengonfigurasi alur middleware yang membungkus middleware yang ditambahkan Program.cs dengan menggunakan UseRouting dan UseEndpoints. Untuk informasi selengkapnya, lihat Routing di ASP.NET Core.

Perutean konvensional

Gunakan routing konvensional dengan controller dan tampilan. Rute default adalah:

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

Kode 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 opsional id. Yang di ? dalam {id?} membuatnya opsional. id merepresentasikan entitas model.

Dengan menggunakan rute default ini, jalur URL:

  • /Products/List dipetakan ke tindakan ProductsController.List.
  • /Blog/Article/17 memetakan ke BlogController.Article dan biasanya model mengikat id parameter ke 17.

Pemetaan ini:

  • Didasarkan hanya pada nama pengontrol dan tindakan .
  • Tidak didasarkan pada namespace, lokasi file sumber, atau parameter metode.

Dengan menggunakan perutean konvensional dengan rute default, Anda tidak perlu membuat pola URL baru untuk setiap tindakan. Untuk aplikasi dengan tindakan bergaya CRUD, memiliki konsistensi URL di seluruh pengontrol:

  • Membantu menyederhanakan kode.
  • Membuat UI lebih mudah diprediksi.

Warning

Templat rute mendefinisikan id sebagai opsional. Tindakan dapat dijalankan tanpa ID opsional yang disediakan sebagai bagian dari URL. Umumnya, ketika id dihilangkan dari URL:

  • Pengikatan model mengatur id menjadi 0.
  • Tidak ada entitas yang ditemukan dalam database yang cocok id == 0.

Routing atribut memberikan kontrol terperinci untuk membuat ID diwajibkan untuk beberapa tindakan dan tidak 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 konvensional default {controller=Home}/{action=Index}/{id?}:

  • 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 urutan ke titik akhir mereka berdasarkan urutan pemanggilan.

Perutean titik akhir di ASP.NET Core:

  • Tidak memiliki konsep rute.
  • Tidak memberikan jaminan urutan untuk pelaksanaan ekstensibilitas. Semua titik akhir diproses sekaligus.

Aktifkan pencatatan log untuk melihat bagaimana implementasi perutean bawaan, seperti Route, mencocokkan permintaan.

Perutean atribut dijelaskan nanti dalam dokumen ini.

Beberapa rute konvensional

Anda dapat mengonfigurasi beberapa rute konvensional dengan menambahkan lebih banyak panggilan ke MapControllerRoute dan MapAreaControllerRoute. Saat menambahkan panggilan ini, Anda dapat 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 adalah rute konvensional khusus karena:

Karena templat "blog/{*article}" rute tidak menyertakan controller dan action sebagai parameter:

  • Mereka hanya dapat memiliki nilai default { controller = "Blog", action = "Article" }.
  • Rute ini selalu mengarah ke tindakan BlogController.Article.

/Blog, /Blog/Article, dan /Blog/{any-string} adalah satu-satunya jalur URL yang cocok dengan rute blog.

Dalam contoh sebelumnya:

  • Rute blog memiliki prioritas yang lebih tinggi dalam pencocokan daripada rute default karena Anda menambahkannya terlebih dahulu.
  • Ini adalah contoh routing bergaya Slug yang biasanya menyertakan nama artikel sebagai bagian dari URL.

Warning

Dalam ASP.NET Core, perutean tidak:

  • Tentukan konsep yang disebut rute. UseRouting menambahkan pencocokan rute ke alur middleware. Middleware UseRouting 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 Routing untuk materi referensi mengenai routing.

Urutan perutean konvensional

Perutean konvensional hanya cocok dengan kombinasi tindakan dan pengontrol yang ditentukan aplikasi. Pendekatan ini menyederhanakan kasus di mana rute konvensional tumpang tindih. Saat Anda menambahkan rute dengan menggunakan MapControllerRoute, MapDefaultControllerRoute, dan MapAreaControllerRoute, titik akhir secara otomatis mendapatkan nilai pesanan berdasarkan urutan anda memanggil metode ini. Kecocokan dari rute yang muncul sebelumnya dalam daftar memiliki prioritas yang lebih tinggi. Perutean konvensional tergantung pada urutan. Secara umum, tempatkan rute dengan area sebelumnya karena lebih spesifik daripada rute tanpa area. Rute konvensional khusus dengan parameter rute catch-all, seperti {*article}, dapat membuat rute terlalu serakah. Rute rakus cocok dengan URL yang sebenarnya ingin Anda cocokkan dengan rute lain. Letakkan rute serakah nanti di tabel rute untuk mencegah kecocokan serakah.

Warning

Parameter catch-all mungkin mencocokkan rute secara tidak tepat karena adanya kesalahan dalam perutean. Aplikasi yang terpengaruh oleh bug ini memiliki karakteristik berikut:

  • Rute serba guna, misalnya, {**slug}"
  • Rute catch-all gagal mencocokkan permintaan yang seharusnya sesuai.
  • Menghapus rute lain membuat rute catch-all mulai berfungsi.

Lihat bug GitHub 18677 dan 16579 misalnya kasus yang mengenai bug ini.

Perbaikan opsional untuk bug ini terkandung dalam SDK .NET Core 3.1.301 atau 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 terhubung melalui perutean, perutean harus melakukan salah satu langkah 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 HTTP POST.
  • Edit(int) dipilih ketika kata kerja HTTP adalah hal lain. Edit(int) umumnya dipanggil melalui GET.

HttpPostAttribute, [HttpPost], disediakan untuk perutean sehingga dapat memilih berdasarkan metode HTTP permintaan. HttpPostAttribute membuat Edit(int, Product) kecocokan yang lebih baik daripada Edit(int).

Penting untuk memahami peran atribut seperti HttpPostAttribute. Atribut serupa didefinisikan untuk HTTP verb lainnya. Dalam perutean konvensional, aksi sering menggunakan nama aksi yang sama saat menjadi bagian dari alur kerja menampilkan formulir dan mengirimkannya. Misalnya, lihat Periksa dua metode tindakan Edit.

Jika perutean tidak dapat memilih kandidat terbaik, perutean akan melemparkan AmbiguousMatchException dan mencantumkan beberapa endpoint 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 memberikan nama logis kepada rute tersebut. 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 direpresentasikan dalam routing sebagai IEndpointNameMetadata. Istilah nama rute dan nama titik akhir:

  • Bisa dipertukarkan.
  • Mana yang digunakan dalam dokumentasi dan kode tergantung pada API yang dijelaskan.

Pengaturan rute berbasis atribut untuk REST APIs

REST API harus menggunakan perutean atribut untuk memodelkan fungsionalitas aplikasi sebagai sekumpulan sumber daya, di mana operasi diwakili oleh verba 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, Anda memanggil MapControllers untuk memetakan pengontrol yang dirutekan 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);
    }
}

Aksi HomeController.Index dijalankan untuk salah satu dari 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, nama pengontrol dan aksi tidak berpengaruh dalam pencocokan aksi, kecuali jika digunakan penggantian token. 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, templat metode Index harus menambahkan / atau ~/ ke awal 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 disediakan

Kata kunci berikut adalah nama parameter rute yang dicadangkan saat menggunakan Controller atau Razor Halaman:

  • action
  • area
  • controller
  • handler
  • page

Menggunakan page sebagai parameter rute dengan perutean atribut adalah kesalahan umum. Pilihan ini 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);
    }
}

Pembuatan URL menggunakan nama parameter khusus ini 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

Jangan gunakan kata kunci ini untuk pembuatan tautan, parameter terikat model, atau properti tingkat atas.

Templat kata kerja HTTP

ASP.NET Core menyertakan templat kata kerja HTTP berikut:

Templat rute

ASP.NET Core menyertakan templat rute berikut:

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.
  • Aksi GetProduct menyertakan templat "{id}", sehingga id ditambahkan ke bagian akhir templat "api/[controller]" pada pengontrol. Templat metode ini 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}, dan sebagainya.
    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }
    
  • Tindakan GetIntProduct berisi "int/{id:int}" template. Bagian :int templat membatasi id nilai rute ke string yang dapat dikonversi menjadi bilangan bulat. Permintaan GET ke /api/test2/int/abc:
    • Tidak sesuai dengan tindakan ini.
    • Mengembalikan error 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 membatasi id 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. Parameter id metode adalah bilangan bulat.
    • Mengembalikan 400 Permintaan Tidak Valid karena pengikatan model gagal mengonversi 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 /products3URL :

  • Tindakan MyProductsController.ListProducts berjalan ketika verba HTTP adalah GET.
  • Tindakan MyProductsController.CreateProduct berjalan ketika verba HTTP adalah POST.

Saat membangun REST API, jarang anda perlu menggunakan [Route(...)] pada metode tindakan karena tindakan menerima semua metode HTTP. Gunakan 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 yang terkait dengan 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. Desain ini berarti bahwa banyak operasi, seperti 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 /products2URL .

Dengan atribut [Consumes], suatu tindakan dapat membatasi jenis konten permintaan yang didukung. Untuk informasi selengkapnya, lihat Menentukan jenis konten permintaan yang didukung dengan atribut Consumes.

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);
    }
}

Gunakan nama rute untuk menghasilkan URL berdasarkan rute tertentu. Nama rute:

  • Tidak memiliki dampak pada perilaku perutean pencocokan URL.
  • Hanya digunakan untuk pembuatan URL.

Nama rute harus unik di seluruh aplikasi.

Bandingkan kode sebelumnya dengan rute default konvensional, yang mendefinisikan parameter id 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, gabungkan atribut rute pada pengontrol dengan atribut rute pada tindakan individual. Templat rute yang Anda tentukan pada pengontrol telah ditambahkan ke templat rute pada tindakan. Saat Anda menempatkan atribut rute pada pengontrol, 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:

  • URL jalur /products dapat cocok dengan ProductsApi.ListProducts
  • Jalur URL /products/5 dapat disesuaikan dengan ProductsApi.GetProduct(int).

Kedua tindakan ini hanya cocok dengan HTTP GET karena ditandai dengan [HttpGet] atribut .

Templat rute yang Anda terapkan pada aksi dan yang diawali dengan / atau ~/ tidak digabungkan dengan templat rute yang Anda terapkan pada kontroler. 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.

Attribute Dikombinasikan dengan [Route("Home")] Menentukan templat rute
[Route("")] Yes "Home"
[Route("Index")] Yes "Home/Index"
[Route("/")] No ""
[Route("About")] Yes "Home/About"

Urutan rute atribut

Perutean membangun pohon dan mencocokkan semua titik akhir secara bersamaan:

  • Entri rute berperilaku seakan 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 urutan menggunakan properti Order. Semua atribut rute yang disediakan kerangka kerja meliputi Order . Rute diproses sesuai dengan urutan naik dari properti Order. Urutan defaultnya adalah 0. Mengatur rute menggunakan Order = -1 dieksekusi sebelum rute yang tidak mengatur urutan. Mengatur rute menggunakan Order = 1 dijalankan setelah pengurutan rute default.

Hindari tergantung pada Order. Jika ruang URL aplikasi memerlukan urutan nilai yang eksplisit untuk dapat merutekan dengan benar, kemungkinan hal itu juga membingungkan klien. 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 penggantian biasanya lebih sederhana daripada menerapkan properti Order.

Pertimbangkan dua pengontrol berikut yang keduanya menentukan pencocokan rute /home:

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);
    }
}

Meminta /home dengan menggunakan kode sebelumnya akan memunculkan pengecualian seperti berikut:

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 endpoint. Untuk sampai ke MyDemoController.MyIndex, minta /home/MyIndex. Catatan:

  • Kode yang tertera sebelumnya adalah contoh desain perutean yang kurang baik. Ini menunjukkan properti Order.
  • Properti Order hanya menyelesaikan ambiguitas. Templat tersebut tidak dapat dicocokkan. Lebih baik menghapus template [Route("Home")].

Untuk informasi tentang urutan rute dengan Razor Pages, lihat Razor Rute Pages dan konvensi aplikasi: Urutan rute.

Dalam beberapa kasus, kesalahan HTTP 500 dikembalikan dengan rute ambigu. Gunakan pencatatan log 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 Anda menentukan rute:

[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 berperilaku 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.

Anda juga dapat menggabungkan rute atribut dengan pewarisan. Kombinasi ini sangat efektif saat Anda menerapkan 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

Anda dapat menyesuaikan penggantian token dengan 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:

  • Terapkan transformator parameter ke semua rute atribut dalam aplikasi.
  • Menyesuaikan nilai token rute atribut ketika mereka diganti.
public class SubscriptionManagementController : Controller
{
    [HttpGet("[controller]/[action]")]
    public IActionResult ListAll()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Metode sebelumnya ListAll cocok /subscription-management/list-alldengan .

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();

Untuk definisi slug, lihat dokumentasi web MDN tentang Slug.

Warning

Saat menggunakan System.Text.RegularExpressions untuk memproses masukan yang tidak tepercaya, berikan batas waktu. Pengguna berbahaya mampu memberikan input ke RegularExpressions yang menyebabkan Serangan Penolakan Layanan. API kerangka kerja ASP.NET Core yang menggunakan RegularExpressions menetapkan 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 tergabung dengan masing-masing atribut rute pada metode aksi.

[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 penentuan batasan rute HTTP verb menerapkan IActionConstraint.

Saat lebih dari satu atribut rute yang menerapkan IActionConstraint ditempatkan pada aksi:

  • 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.

Untuk deskripsi terperinci tentang sintaks templat rute, lihat Referensi Templat Rute.

Atribut rute kustom menggunakan IRouteTemplateProvider

Semua atribut rute menerapkan IRouteTemplateProvider. ASP.NET Core Runtime:

  • Mencari atribut pada kelas pengontrol dan metode tindakan saat aplikasi dimulai.
  • Menggunakan atribut yang menerapkan IRouteTemplateProvider untuk membangun kumpulan 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 atribut rute disediakan oleh implementasi IRouteTemplateProvider. Conventions:

  • Bisa ditulis untuk memodifikasi model aplikasi agar dapat disesuaikan dengan perilaku perutean.
  • Dibaca saat pengaktifan aplikasi.

Bagian ini menunjukkan contoh dasar penyesuaian pengarahan dengan 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 didirutekan menggunakan atribut.
  • Mengatur templat pengontrol berdasarkan namespace, dengan basis namespace 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:

  • Basis dari namespace adalah My.Application.
  • Nama lengkap pengontrol sebelumnya adalah My.Application.Admin.Controllers.UsersController.
  • Mengatur templat pengontrol dari NamespaceRoutingConvention ke Admin/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 menggabungkan penggunaan rute konvensional dan rute atribut. Biasanya, Anda menggunakan rute konvensional untuk pengontrol yang menyajikan halaman HTML ke browser, dan perutean atribut untuk pengontrol yang melayani API REST.

Aksi dirutekan secara konvensional atau dengan atribut. Saat Anda menempatkan rute pada pengontrol atau tindakan, rute tersebut menjadi rute atribut. Anda tidak dapat menjangkau tindakan yang menentukan rute atribut 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 routing yang sama.

Perutean dengan karakter khusus

Pengaturan rute 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 Encoded
/ %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 ambien

Aplikasi dapat menggunakan fitur pembuatan URL pengalihan untuk menghasilkan tautan URL menuju aksi. Dengan menghasilkan URL, kita dapat menghilangkan pengkodean tetap URL, 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 pada properti Url 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 bawaan, nilai variabel url adalah string jalur URL /UrlGeneration/Destination. Routing membuat jalur URL ini dengan menggabungkan:

  • Nilai rute dari permintaan saat ini, yang disebut nilai ambien.
  • 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 akan digantikan nilainya dengan mencocokkan nama dengan nilai serta nilai lingkungan. 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 dan action biasanya muncul dalam templat tersebut. Ini berfungsi karena URL yang dipilih oleh routing mengikuti 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 juga memiliki serangkaian metode yang sesuai pada LinkGenerator.

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 Anda menggunakan Url.Action, runtime menyediakan nilai rute saat ini untuk controller dan action:

  • Nilai dari controller dan action merupakan bagian dari nilai ambient dan nilai. Metode Url.Action ini selalu menggunakan nilai action saat ini dan dan controller 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, dan pembuatan URL gagal.
  • Anda harus menentukan nilai yang diinginkan dan cd untuk menghasilkan URL.

Anda mungkin mengharapkan untuk mengalami masalah ini dengan default rute {controller}/{action}/{id?}. 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 }). Nilai objek rute:

  • Menurut konvensi biasanya merupakan objek jenis anonim.
  • Dapat berupa IDictionary<> atau POCO).

Setiap nilai rute tambahan yang tidak cocok dengan parameter rute masuk ke 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 opsi berikut:

  • Beban berlebih yang menerima protocol. Misalnya, kode sebelumnya.
  • LinkGenerator.GetUriByAction, yang menghasilkan URI absolut secara default.

Hasilkan URL menurut rute

Kode sebelumnya menunjukkan cara membuat URL dengan meneruskan parameter nama pengontrol dan tindakan. IUrlHelper juga menyediakan keluarga metode Url.RouteUrl . Metode ini mirip dengan Url.Action, tetapi tidak menyalin nilai action dan controller saat ini ke dalam 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

IHtmlHelper HtmlHelper 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 elemen ini menggunakan IUrlHelper untuk implementasi mereka. Untuk informasi selengkapnya, lihat Menandai Pembantu dalam formulir.

Di dalam view, IUrlHelper tersedia melalui properti Url untuk pembuatan URL ad hoc apa pun yang tidak dicakup oleh metode-metode sebelumnya.

Pembuatan URL dalam Hasil Tindakan

Contoh sebelumnya menunjukkan cara menggunakan IUrlHelper di 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 yang didedikasikan

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 dengan menggunakan default rute, tetapi mengapa? Anda mungkin menebak bahwa nilai rute { controller = Home, action = Index } sudah cukup untuk menghasilkan URL dengan blog, dan hasilnya adalah /blog?action=Index&controller=Home.

Rute konvensional yang berdedikasi bergantung pada perilaku khusus dari nilai default yang tidak memiliki parameter rute yang sesuai untuk mencegah rutenya terlalu agresif dalam 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 diberikan harus sesuai dengan nilai bawaan. Pembuatan URL menggunakan blog gagal karena nilainya { controller = Home, action = Index } tidak cocok { controller = Blog, action = Article }. Routing kemudian kembali pada upaya default, yang berhasil.

Areas

Area adalah fitur MVC yang digunakan untuk mengatur fungsionalitas terkait ke dalam grup sebagai terpisah:

  • Penerusan namespace untuk aksi pengontrol.
  • Struktur folder untuk tampilan.

Dengan menggunakan area, sebuah aplikasi dapat memiliki beberapa pengontrol dengan nama yang sama, selama setiap pengontrol berada di area yang berbeda. Menggunakan area menciptakan hierarki untuk keperluan perutean dengan menambahkan satu parameter rute lagi, area ke controller dan action. Bagian ini membahas bagaimana perutean berinteraksi dengan wilayah. Lihat Area untuk informasi terperinci tentang bagaimana area digunakan dalam tampilan.

Contoh berikut mengonfigurasi MVC untuk menggunakan rute konvensional default dan sebuah rute area untuk 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, rute "blog_route" menghasilkan nilai-nilai rute { area = Blog, controller = Users, action = AddUser }. Nilai area rute berasal dari nilai default untuk area. Rute yang dibuat oleh MapAreaControllerRoute setara dengan kode berikut:

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 sebuah rute menggunakan nilai default dan batasan untuk area menggunakan nama area yang disediakan, dalam hal ini Blog. Nilai default memastikan bahwa rute selalu menghasilkan { area = Blog, ... }, dan batasan memerlukan nilai { area = Blog, ... } untuk pembuatan URL.

Perutean konvensional bergantung pada pesanan. Secara umum, tempatkan rute dengan area sebelumnya karena lebih spesifik daripada rute tanpa area.

Menggunakan contoh sebelumnya, nilai-nilai rute { area = Blog, controller = Users, action = AddUser } sesuai 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 Blog. Controller tanpa atribut [Area] bukan anggota area mana pun, dan tidak akan cocok ketika perutean menyediakan nilai rute area. Dalam contoh berikut, hanya pengontrol pertama yang tercantum yang dapat cocok dengan nilai rute { area = Blog, controller = Users, action = AddUser }.

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}");
        }
    }
}

Untuk kelengkapan, namespace setiap pengontrol ditampilkan di sini. Jika pengontrol sebelumnya menggunakan namespace yang sama, kesalahan kompilator akan dihasilkan. Namespace kelas tidak berpengaruh pada perutean MVC.

Dua pengontrol yang pertama adalah anggota area dan hanya sesuai ketika nama area masing-masing disediakan oleh nilai rute area. 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 ambien yang dapat digunakan perutean untuk pembuatan URL. Ini berarti bahwa secara default area bertindak melekat untuk pembuatan URL sebagaimana yang diperlihatkan dalam contoh 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

Pemecahan Masalah Diagnostik

Untuk output diagnostik perutean terperinci, atur Logging:LogLevel:Microsoft ke Debug. Di lingkungan Development, atur tingkat log di appsettings.Development.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Debug",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

ASP.NET Core menggunakan pengontrol Perutean middleware untuk mencocokkan URL permintaan masuk dan memetakannya ke aksi. 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.

Aksi dirutekan secara konvensional atau dengan 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 digunakan dengan REST API. Jika Anda terutama tertarik pada perutean untuk REST API, lanjutkan 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. Diperbolehkan menggunakan pengontrol dengan versi perutean sebelumnya untuk tujuan kompatibilitas. Lihat panduan migrasi 2.2-3.0 untuk petunjuknya. Lihat dokumen versi 2.2 ini untuk bahan referensi pada sistem perutean lama.

Menyiapkan rute konvensional

Startup.Configure biasanya memiliki kode yang mirip dengan contoh berikut 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 dinamai rute default. Sebagian besar aplikasi dengan pengontrol dan tampilan menggunakan templat rute yang mirip dengan default rute. REST API harus menggunakan perutean atribut.

Templat "{controller=Home}/{action=Index}/{id?}"rute :

  • Cocok dengan jalur URL seperti /Products/Details/5

  • Mengekstrak nilai rute { controller = Products, action = Details, id = 5 } dengan men-tokenisasi jalur. Ekstraksi nilai rute menghasilkan kecocokan jika aplikasi memiliki pengontrol bernama ProductsController dan Details 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 nilai id = 5 untuk mengatur parameter ke id5. Untuk informasi selengkapnya, lihat Pengikatan Model.

  • {controller=Home} mendefinisikan Home sebagai controller default.

  • {action=Index} mendefinisikan Index sebagai action default.

  • Karakter ? dalam {id?} mendefinisikan id sebagai opsional.

  • Parameter rute default dan opsional tidak perlu ada di jalur URL untuk kecocokan. Untuk deskripsi terperinci tentang sintaks templat rute, lihat Referensi Templat Rute.

  • Cocok dengan jalur URL /.

  • Menghasilkan nilai rute { controller = Home, action = Index }.

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 tindakan HomeController dan Index.

public class HomeController : Controller
{
  public IActionResult Index() { ... }
}

Menggunakan definisi pengontrol sebelumnya dan templat rute, HomeController.Index tindakan berjalan untuk jalur URL berikut:

  • /Home/Index/17
  • /Home/Index
  • /Home
  • /

Jalur URL / menggunakan templat rute kontroler default Home dan aksi Index. Jalur URL /Home menggunakan tindakan default Index templat rute.

Metode kenyamanan MapDefaultControllerRoute:

endpoints.MapDefaultControllerRoute();

Replaces:

endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");

Important

Perutean dikonfigurasi menggunakan middleware UseRouting, MapControllerRoute, dan MapAreaControllerRoute. Untuk menggunakan pengontrol:

Perutean konvensional

Gunakan routing konvensional dengan controller dan tampilan. Rute default adalah:

endpoints.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

Kode 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 opsional id. Yang di ? dalam {id?} membuatnya opsional. id merepresentasikan entitas model.

Dengan menggunakan rute default ini, jalur URL:

  • /Products/List dipetakan ke tindakan ProductsController.List.
  • /Blog/Article/17 memetakan ke BlogController.Article dan biasanya model mengikat id parameter ke 17.

Pemetaan ini:

  • Didasarkan hanya pada nama pengontrol dan tindakan .
  • Tidak didasarkan pada namespace, lokasi file sumber, atau parameter metode.

Dengan menggunakan perutean konvensional dengan rute default, Anda tidak perlu membuat pola URL baru untuk setiap tindakan. Untuk aplikasi dengan tindakan bergaya CRUD, memiliki konsistensi URL di seluruh pengontrol:

  • Membantu menyederhanakan kode.
  • Membuat UI lebih mudah diprediksi.

Warning

Templat rute mendefinisikan id sebagai opsional. Tindakan dapat dijalankan tanpa ID opsional yang disediakan sebagai bagian dari URL. Umumnya, ketika id dihilangkan dari URL:

  • Pengikatan model mengatur id menjadi 0.
  • Tidak ada entitas yang ditemukan dalam database yang cocok id == 0.

Routing atribut memberikan kontrol terperinci untuk membuat ID diwajibkan untuk beberapa tindakan dan tidak 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 konvensional default {controller=Home}/{action=Index}/{id?}:

  • 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 urutan ke titik akhir mereka berdasarkan urutan pemanggilan.

Perutean titik akhir di ASP.NET Core 3.0 atau yang lebih baru:

  • Tidak memiliki konsep rute.
  • Tidak memberikan jaminan urutan untuk pelaksanaan ekstensibilitas. Semua titik akhir diproses sekaligus.

Aktifkan pencatatan log 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 adalah rute konvensional khusus karena:

Karena templat "blog/{*article}" rute tidak menyertakan controller dan action sebagai parameter:

  • Mereka hanya dapat memiliki nilai default { controller = "Blog", action = "Article" }.
  • Rute ini selalu mengarah ke tindakan BlogController.Article.

/Blog, /Blog/Article, dan /Blog/{any-string} adalah satu-satunya jalur URL yang cocok dengan rute blog.

Dalam contoh sebelumnya:

  • Rute blog memiliki prioritas yang lebih tinggi dalam pencocokan daripada rute default karena Anda menambahkannya terlebih dahulu.
  • Ini adalah contoh routing bergaya Slug yang biasanya menyertakan nama artikel sebagai bagian dari URL.

Warning

Di ASP.NET Core 3.0 atau yang lebih baru, perutean tidak:

  • Tentukan konsep yang disebut rute. UseRouting menambahkan pencocokan rute ke alur middleware. Middleware UseRouting 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 Routing untuk materi referensi mengenai routing.

Urutan perutean konvensional

Perutean konvensional hanya cocok dengan kombinasi tindakan dan pengontrol yang ditentukan aplikasi. Pendekatan ini menyederhanakan kasus di mana rute konvensional tumpang tindih. Saat Anda menambahkan rute dengan menggunakan MapControllerRoute, MapDefaultControllerRoute, dan MapAreaControllerRoute, titik akhir secara otomatis mendapatkan nilai pesanan berdasarkan urutan anda memanggil metode ini. Kecocokan dari rute yang muncul sebelumnya dalam daftar memiliki prioritas yang lebih tinggi. Perutean konvensional tergantung pada urutan. Secara umum, tempatkan rute dengan area sebelumnya karena lebih spesifik daripada rute tanpa area. Rute konvensional khusus dengan parameter rute catch-all, seperti {*article}, dapat membuat rute terlalu serakah. Rute rakus cocok dengan URL yang sebenarnya ingin Anda cocokkan dengan rute lain. Letakkan rute serakah nanti di tabel rute untuk mencegah kecocokan serakah.

Warning

Parameter catch-all mungkin mencocokkan rute secara tidak tepat karena adanya kesalahan dalam perutean. Aplikasi yang terpengaruh oleh bug ini memiliki karakteristik berikut:

  • Rute serba guna, misalnya, {**slug}"
  • Rute catch-all gagal mencocokkan permintaan yang seharusnya sesuai.
  • Menghapus rute lain membuat rute catch-all mulai berfungsi.

Lihat bug GitHub 18677 dan 16579 misalnya kasus yang mengenai bug ini.

Perbaikan opsional untuk bug ini terkandung dalam SDK .NET Core 3.1.301 atau 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 terhubung melalui perutean, perutean harus melakukan salah satu langkah 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 HTTP POST.
  • Edit(int) dipilih ketika kata kerja HTTP adalah hal lain. Edit(int) umumnya dipanggil melalui GET.

HttpPostAttribute, [HttpPost], disediakan untuk perutean sehingga dapat memilih berdasarkan metode HTTP permintaan. HttpPostAttribute membuat Edit(int, Product) kecocokan yang lebih baik daripada Edit(int).

Penting untuk memahami peran atribut seperti HttpPostAttribute. Atribut serupa didefinisikan untuk HTTP verb lainnya. Dalam perutean konvensional, biasanya tindakan menggunakan nama tindakan yang sama ketika menjadi bagian dari formulir tampilan, pengiriman formulir alur kerja. Misalnya, lihat Periksa dua metode tindakan Edit.

Jika perutean tidak dapat memilih kandidat terbaik, perutean akan melemparkan AmbiguousMatchException dan mencantumkan beberapa endpoint 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 memberikan nama logis kepada rute tersebut. 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 direpresentasikan dalam routing sebagai IEndpointNameMetadata. Istilah nama rute dan nama titik akhir:

  • Bisa dipertukarkan.
  • Mana yang digunakan dalam dokumentasi dan kode tergantung pada API yang dijelaskan.

Pengaturan rute berbasis atribut untuk REST APIs

REST API harus menggunakan perutean atribut untuk memodelkan fungsionalitas aplikasi sebagai sekumpulan sumber daya, di mana operasi diwakili oleh verba 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 di dalam UseEndpoints untuk memetakan pengontrol yang menggunakan 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);
    }
}

Aksi HomeController.Index dijalankan untuk salah satu dari 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, nama pengontrol dan aksi tidak berpengaruh dalam pencocokan aksi, kecuali jika digunakan penggantian token. 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, templat metode Index harus menambahkan / atau ~/ ke awal 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 disediakan

Kata kunci berikut adalah nama parameter rute yang dicadangkan saat menggunakan Controller atau Razor Halaman:

  • action
  • area
  • controller
  • handler
  • page

Menggunakan page sebagai parameter rute dengan perutean atribut adalah kesalahan umum. Pilihan ini 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);
    }
}

Pembuatan URL menggunakan nama parameter khusus ini 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

Jangan gunakan kata kunci ini untuk pembuatan tautan, parameter terikat model, atau properti tingkat atas.

Templat kata kerja HTTP

ASP.NET Core menyertakan templat kata kerja HTTP berikut:

Templat rute

ASP.NET Core menyertakan templat rute berikut:

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.
  • Aksi GetProduct menyertakan templat "{id}", sehingga id ditambahkan ke bagian akhir templat "api/[controller]" pada pengontrol. Templat metode ini 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}, dan sebagainya.
    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }
    
  • Tindakan GetIntProduct berisi "int/{id:int}" template. Bagian :int templat membatasi id nilai rute ke string yang dapat dikonversi menjadi bilangan bulat. Permintaan GET ke /api/test2/int/abc:
    • Tidak sesuai dengan tindakan ini.
    • Mengembalikan error 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 membatasi id 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. Parameter id metode adalah bilangan bulat.
    • Mengembalikan 400 Permintaan Tidak Valid karena pengikatan model gagal mengonversi 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 /products3URL :

  • Tindakan MyProductsController.ListProducts berjalan ketika verba HTTP adalah GET.
  • Tindakan MyProductsController.CreateProduct berjalan ketika verba HTTP adalah POST.

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 jelas tentang apa yang didukung oleh API Anda. REST Klien API diharapkan mengetahui jalur dan kata kerja HTTP yang terkait dengan 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. Desain ini berarti bahwa banyak operasi, seperti 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 /products2URL .

Dengan atribut [Consumes], suatu tindakan dapat membatasi jenis konten permintaan yang didukung. Untuk informasi selengkapnya, lihat Menentukan jenis konten permintaan yang didukung dengan atribut Consumes.

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);
    }
}

Gunakan nama rute untuk menghasilkan URL berdasarkan rute tertentu. Nama rute:

  • Tidak memiliki dampak pada perilaku perutean pencocokan URL.
  • Hanya digunakan untuk pembuatan URL.

Nama rute harus unik di seluruh aplikasi.

Bandingkan kode sebelumnya dengan rute default konvensional, yang mendefinisikan parameter id 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, gabungkan atribut rute pada pengontrol dengan atribut rute pada tindakan individual. Templat rute yang Anda tentukan pada pengontrol telah ditambahkan ke templat rute pada tindakan. Saat Anda menempatkan atribut rute pada pengontrol, 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:

  • URL jalur /products dapat cocok dengan ProductsApi.ListProducts
  • Jalur URL /products/5 dapat disesuaikan dengan ProductsApi.GetProduct(int).

Kedua tindakan ini hanya cocok dengan HTTP GET karena ditandai dengan [HttpGet] atribut .

Templat rute yang Anda terapkan pada aksi dan yang diawali dengan / atau ~/ tidak digabungkan dengan templat rute yang Anda terapkan pada kontroler. 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.

Attribute Dikombinasikan dengan [Route("Home")] Menentukan templat rute
[Route("")] Yes "Home"
[Route("Index")] Yes "Home/Index"
[Route("/")] No ""
[Route("About")] Yes "Home/About"

Urutan rute atribut

Perutean membangun pohon dan mencocokkan semua titik akhir secara bersamaan:

  • Entri rute berperilaku seakan 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 urutan menggunakan properti Order. Semua atribut rute yang disediakan kerangka kerja meliputi Order . Rute diproses sesuai dengan urutan naik dari properti Order. Urutan defaultnya adalah 0. Mengatur rute menggunakan Order = -1 dieksekusi sebelum rute yang tidak mengatur urutan. Mengatur rute menggunakan Order = 1 dijalankan setelah pengurutan rute default.

Hindari tergantung pada Order. Jika ruang URL aplikasi memerlukan urutan nilai yang eksplisit untuk dapat merutekan dengan benar, kemungkinan hal itu juga membingungkan klien. 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 penggantian biasanya lebih sederhana daripada menerapkan properti Order.

Pertimbangkan dua pengontrol berikut yang keduanya menentukan pencocokan rute /home:

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);
    }
}

Meminta /home dengan menggunakan kode sebelumnya akan memunculkan pengecualian seperti berikut:

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 endpoint. Untuk sampai ke MyDemoController.MyIndex, minta /home/MyIndex. Catatan:

  • Kode yang tertera sebelumnya adalah contoh desain perutean yang kurang baik. Ini menunjukkan properti Order.
  • Properti Order hanya menyelesaikan ambiguitas. Templat tersebut tidak dapat dicocokkan. Lebih baik menghapus template [Route("Home")].

Untuk informasi tentang urutan rute dengan Razor Pages, lihat Razor Rute Pages dan konvensi aplikasi: Urutan rute.

Dalam beberapa kasus, kesalahan HTTP 500 dikembalikan dengan rute ambigu. Gunakan pencatatan log 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 Anda menentukan rute:

[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 berperilaku 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.

Anda juga dapat menggabungkan rute atribut dengan pewarisan. Kombinasi ini sangat efektif saat Anda menerapkan 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

Anda dapat menyesuaikan penggantian token dengan 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:

  • Terapkan transformator parameter ke semua rute atribut dalam aplikasi.
  • Menyesuaikan nilai token rute atribut ketika mereka diganti.
public class SubscriptionManagementController : Controller
{
    [HttpGet("[controller]/[action]")]
    public IActionResult ListAll()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Metode sebelumnya ListAll cocok /subscription-management/list-alldengan .

RouteTokenTransformerConvention terdaftar sebagai opsi di ConfigureServices.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
    {
        options.Conventions.Add(new RouteTokenTransformerConvention(
                                     new SlugifyParameterTransformer()));
    });
}

Untuk definisi slug, lihat dokumentasi web MDN tentang Slug.

Warning

Saat menggunakan System.Text.RegularExpressions untuk memproses masukan yang tidak tepercaya, berikan batas waktu. Pengguna berbahaya mampu memberikan input ke RegularExpressions yang menyebabkan Serangan Penolakan Layanan. API kerangka kerja ASP.NET Core yang menggunakan RegularExpressions menetapkan 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 tergabung dengan masing-masing atribut rute pada metode aksi.

[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 penentuan batasan rute HTTP verb menerapkan IActionConstraint.

Saat lebih dari satu atribut rute yang menerapkan IActionConstraint ditempatkan pada aksi:

  • 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.

Untuk deskripsi terperinci tentang sintaks templat rute, lihat Referensi Templat Rute.

Atribut rute kustom menggunakan IRouteTemplateProvider

Semua atribut rute menerapkan IRouteTemplateProvider. ASP.NET Core Runtime:

  • Mencari atribut pada kelas pengontrol dan metode tindakan saat aplikasi dimulai.
  • Menggunakan atribut yang menerapkan IRouteTemplateProvider untuk membangun kumpulan 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 pada awal sistem berjalan.
  • 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 atribut rute disediakan oleh implementasi IRouteTemplateProvider. Conventions:

  • Bisa ditulis untuk memodifikasi model aplikasi agar dapat disesuaikan dengan perilaku perutean.
  • Dibaca saat pengaktifan aplikasi.

Bagian ini menunjukkan contoh dasar penyesuaian pengarahan dengan 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 didirutekan menggunakan atribut.
  • Mengatur templat pengontrol berdasarkan namespace, dengan basis namespace 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:

  • Basis dari namespace adalah My.Application.
  • Nama lengkap pengontrol sebelumnya adalah My.Application.Admin.Controllers.UsersController.
  • Mengatur templat pengontrol dari NamespaceRoutingConvention ke Admin/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 menggabungkan penggunaan rute konvensional dan rute atribut. Biasanya, Anda menggunakan rute konvensional untuk pengontrol yang menyajikan halaman HTML ke browser, dan perutean atribut untuk pengontrol yang melayani API REST.

Aksi dirutekan secara konvensional atau dengan atribut. Menempatkan rute pada pengontrol atau tindakan membuatnya menjadi rute atribut. 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 routing yang sama.

Pembuatan URL dan nilai ambien

Aplikasi dapat menggunakan fitur pembuatan URL pengalihan untuk menghasilkan tautan URL menuju aksi. Pembuatan URL menghilangkan kebutuhan untuk meng-hardcode URL, membuat kode lebih tangguh dan mudah dipelihara. 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 pada properti Url 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 bawaan, nilai variabel url adalah string jalur URL /UrlGeneration/Destination. Routing membuat jalur URL ini dengan menggabungkan:

  • Nilai rute dari permintaan saat ini, yang disebut nilai ambien.
  • 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 akan digantikan nilainya dengan mencocokkan nama dengan nilai serta nilai lingkungan. 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 dan action biasanya muncul dalam templat tersebut. Ini berfungsi karena URL yang dipilih oleh routing mengikuti 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 juga memiliki serangkaian metode yang sesuai pada LinkGenerator.

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 Anda menggunakan Url.Action, runtime menyediakan nilai rute saat ini untuk controller dan action:

  • Nilai dari controller dan action merupakan bagian dari nilai ambient dan nilai. Metode Url.Action ini selalu menggunakan nilai action saat ini dan dan controller 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, dan pembuatan URL gagal.
  • Anda harus menentukan nilai yang diinginkan dan cd untuk menghasilkan URL.

Anda mungkin mengharapkan untuk mengalami masalah ini dengan default rute {controller}/{action}/{id?}. 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 }). Nilai objek rute:

  • Menurut konvensi biasanya merupakan objek jenis anonim.
  • Dapat berupa IDictionary<> atau POCO).

Setiap nilai rute tambahan yang tidak cocok dengan parameter rute masuk ke 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 opsi berikut:

  • Beban berlebih yang menerima protocol. Misalnya, kode sebelumnya.
  • LinkGenerator.GetUriByAction, yang menghasilkan URI absolut secara default.

Hasilkan URL menurut rute

Kode sebelumnya menunjukkan cara membuat URL dengan meneruskan parameter nama pengontrol dan tindakan. IUrlHelper juga menyediakan keluarga metode Url.RouteUrl . Metode ini mirip dengan Url.Action, tetapi tidak menyalin nilai action dan controller saat ini ke dalam 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

IHtmlHelper HtmlHelper 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 elemen ini menggunakan IUrlHelper untuk implementasi mereka. Untuk informasi selengkapnya, lihat Menandai Pembantu dalam formulir.

Di dalam view, IUrlHelper tersedia melalui properti Url untuk pembuatan URL ad hoc apa pun yang tidak dicakup oleh metode-metode sebelumnya.

Pembuatan URL dalam Hasil Tindakan

Contoh sebelumnya menunjukkan cara menggunakan IUrlHelper di 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 yang didedikasikan

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 dengan menggunakan default rute, tetapi mengapa? Anda mungkin menebak bahwa nilai rute { controller = Home, action = Index } sudah cukup untuk menghasilkan URL dengan blog, dan hasilnya adalah /blog?action=Index&controller=Home.

Rute konvensional yang berdedikasi bergantung pada perilaku khusus dari nilai default yang tidak memiliki parameter rute yang sesuai untuk mencegah rutenya terlalu agresif dalam 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 diberikan harus sesuai dengan nilai bawaan. Pembuatan URL menggunakan blog gagal karena nilainya { controller = Home, action = Index } tidak cocok { controller = Blog, action = Article }. Routing kemudian kembali pada upaya default, yang berhasil.

Areas

Area adalah fitur MVC yang digunakan untuk mengatur fungsionalitas terkait ke dalam grup sebagai terpisah:

  • Penerusan namespace untuk aksi pengontrol.
  • Struktur folder untuk tampilan.

Dengan menggunakan area, sebuah aplikasi dapat memiliki beberapa pengontrol dengan nama yang sama, selama setiap pengontrol berada di area yang berbeda. Menggunakan area menciptakan hierarki untuk keperluan perutean dengan menambahkan satu parameter rute lagi, area ke controller dan action. Bagian ini membahas bagaimana perutean berinteraksi dengan wilayah. Lihat Area untuk informasi terperinci tentang bagaimana area digunakan dalam tampilan.

Contoh berikut mengonfigurasi MVC untuk menggunakan rute konvensional default dan sebuah rute area untuk 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, rute "blog_route" menghasilkan nilai-nilai rute { area = Blog, controller = Users, action = AddUser }. Nilai area rute berasal dari nilai default untuk area. Rute yang dibuat oleh MapAreaControllerRoute setara dengan kode berikut:

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 sebuah rute menggunakan nilai default dan batasan untuk area menggunakan nama area yang disediakan, dalam hal ini Blog. Nilai default memastikan bahwa rute selalu menghasilkan { area = Blog, ... }, dan batasan memerlukan nilai { area = Blog, ... } untuk pembuatan URL.

Perutean konvensional bergantung pada pesanan. Secara umum, tempatkan rute dengan area sebelumnya karena lebih spesifik daripada rute tanpa area.

Menggunakan contoh sebelumnya, nilai-nilai rute { area = Blog, controller = Users, action = AddUser } sesuai 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 Blog. Controller tanpa atribut [Area] bukan anggota area mana pun, dan tidak akan cocok ketika perutean menyediakan nilai rute area. Dalam contoh berikut, hanya pengontrol pertama yang tercantum yang dapat cocok dengan nilai rute { area = Blog, controller = Users, action = AddUser }.

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}");
        }
    }
}

Untuk kelengkapan, namespace setiap pengontrol ditampilkan di sini. Jika pengontrol sebelumnya menggunakan namespace yang sama, kesalahan kompilator akan dihasilkan. Namespace kelas tidak berpengaruh pada perutean MVC.

Dua pengontrol yang pertama adalah anggota area dan hanya sesuai ketika nama area masing-masing disediakan oleh nilai rute area. 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 ambien yang dapat digunakan perutean untuk pembuatan URL. Ini berarti bahwa secara default area bertindak melekat untuk pembuatan URL sebagaimana yang diperlihatkan dalam contoh 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

Pemecahan Masalah Diagnostik

Untuk output diagnostik perutean terperinci, atur Logging:LogLevel:Microsoft ke Debug. Di lingkungan Development, atur tingkat log di appsettings.Development.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Debug",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}