Bagikan melalui


Pengaturan Rute ke Aksi Pengendali di ASP.NET Core

Oleh Ryan Nowak, Kirk Larkin, dan Rick Anderson

Catatan

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

Peringatan

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

Penting

Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.

Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.

ASP.NET Pengontrol Core menggunakan middleware Perutean untuk mencocokkan URL permintaan masuk dan memetakannya ke tindakan. Templat rute:

  • Didefinisikan saat startup dalam Program.cs atau dalam atribut.
  • Menjelaskan bagaimana jalur URL dicocokkan dengan tindakan.
  • Digunakan untuk menghasilkan URL untuk tautan. Tautan yang dihasilkan biasanya dikembalikan sebagai respons.

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 disebut perutean titik akhir. Diperbolehkan menggunakan pengontrol dengan versi perutean sebelumnya untuk tujuan kompatibilitas. Lihat panduan migrasi 2.2-3.0 untuk petunjuknya.

Menyiapkan rute konvensional

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

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

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

app.Run();

MapControllerRoute digunakan untuk membuat satu rute. Rute tunggal dinamai 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. Lihat Pengikatan Model untuk detail lebih lanjut.

  • {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. Lihat Referensi Templat Rute Route Template Reference untuk deskripsi terperinci tentang sintaks 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 dijalankan untuk jalur URL berikut:

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

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

Metode kenyamanan MapDefaultControllerRoute:

app.MapDefaultControllerRoute();

Menggantikan:

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

Penting

Perutean dikonfigurasi menggunakan UseRouting middleware 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 UseRouting dan UseEndpoints. Untuk informasi lebih lanjut, lihat Perutean di ASP.NET Core.

Perutean konvensional

Perutean konvensional digunakan dengan pengontrol dan tampilan. Rute default :

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

Sebelumnya adalah contoh rute konvensional. Ini disebut perutean konvensional karena menetapkan konvensi untuk jalur URL:

  • Segmen jalur pertama, {controller=Home}, memetakan ke nama pengontrol.
  • Segmen kedua, {action=Index}, memetakan ke nama tindakan .
  • Segmen ketiga, {id?} digunakan untuk opsional id. Yang di ? dalam {id?} membuatnya opsional. id digunakan untuk memetakan ke 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.

Menggunakan perutean konvensional dengan rute default memungkinkan mengembangkan aplikasi tanpa 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.

Peringatan

id dalam kode sebelumnya didefinisikan sebagai opsional oleh templat rute. Tindakan dapat dijalankan tanpa ID opsional yang disediakan sebagai bagian dari URL. Umumnya, ketika id dihilangkan dari URL:

  • id diatur ke 0 berdasarkan pengikatan model.
  • 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 menjamin urutan dalam eksekusi fitur ekstensi, semua titik akhir diproses secara bersamaan.

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 dikonfigurasi dengan menambahkan lebih banyak panggilan ke MapControllerRoute dan MapAreaControllerRoute. Melakukannya memungkinkan menentukan beberapa konvensi, atau menambahkan rute konvensional yang didedikasikan untuk tindakan tertentu, seperti:

app.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
               pattern: "{controller=Home}/{action=Index}/{id?}");

Rute blog dalam kode sebelumnya adalah rute konvensional khusus. Ini disebut rute konvensional khusus karena:

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

  • Mereka hanya dapat memiliki nilai 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.

Contoh sebelumnya:

  • blog rute memiliki prioritas lebih tinggi untuk pencocokan daripada rute default karena ditambahkan terlebih dahulu.
  • Adalah contoh perutean gaya Slug yang biasanya menyertakan nama artikel sebagai bagian dari URL.

Peringatan

Dalam ASP.NET Core, perutean tidak berfungsi seperti ini:

  • Tentukan konsep yang disebut rute. UseRouting menambahkan pencocokan rute pada 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 oleh aplikasi. Ini dimaksudkan untuk menyederhanakan kasus di mana rute konvensional tumpang tindih. Menambahkan rute menggunakan MapControllerRoute, MapDefaultControllerRoute, dan MapAreaControllerRoute secara otomatis menetapkan nilai urutan ke titik akhir mereka berdasarkan urutan pemanggilannya. Kecocokan dari rute yang muncul sebelumnya memiliki prioritas yang lebih tinggi. Perutean konvensional bergantung pada pesanan. Secara umum, rute dengan area harus ditempatkan lebih awal karena lebih spesifik daripada rute tanpa area. Rute konvensional khusus dengan parameter rute catch-all seperti dapat membuat rute terlalu {*article}, yang berarti bahwa rute tersebut cocok dengan URL yang Anda maksudkan untuk dicocokkan dengan rute lain. Letakkan rute serakah nanti di tabel rute untuk mencegah kecocokan serakah.

Peringatan

Parameter catch-all mungkin 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 yang perlu diaktifkan untuk bug ini tersedia dalam .NET Core 3.1.301 atau SDK yang lebih baru. Kode berikut menetapkan sakelar internal yang memperbaiki bug ini:

public static void Main(string[] args)
{
   AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior", 
                         true);
   CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.

Mengatasi tindakan ambigu

Saat dua titik akhir cocok melalui perutean, perutean harus melakukan salah satu hal berikut:

  • Pilih kandidat terbaik.
  • Tampilkan pengecualian.

Contohnya:

public class Products33Controller : Controller
{
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpPost]
    public IActionResult Edit(int id, Product product)
    {
        return ControllerContext.MyDisplayRouteInfo(id, product.name);
    }
}

Pengontrol sebelumnya mendefinisikan dua tindakan yang cocok:

  • Jalur URL /Products33/Edit/17
  • Merutekan data { controller = Products33, action = Edit, id = 17 }.

Ini adalah pola umum untuk pengontrol MVC:

  • Edit(int) menampilkan formulir untuk mengedit produk.
  • Edit(int, Product) memproses formulir yang diposting.

Untuk mengatasi rute yang benar:

  • Edit(int, Product) dipilih ketika permintaan adalah 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, sebuah AmbiguousMatchException akan diberikan, yang mencantumkan beberapa titik akhir yang cocok.

Nama rute konvensional

String "blog" dan "default" dalam contoh berikut adalah nama rute konvensional:

app.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
               pattern: "{controller=Home}/{action=Index}/{id?}");

Nama rute 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, MapControllers dipanggil untuk memetakan pengontrol rute atribut.

Dalam contoh berikut:

  • HomeController cocok dengan sekumpulan URL yang mirip dengan apa yang cocok dengan rute {controller=Home}/{action=Index}/{id?} konvensional default.
public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult Index(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult About(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Tindakan HomeController.Index dijalankan untuk salah satu 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 khusus saat menggunakan Pengontrol atau Razor Halaman.

  • action
  • area
  • controller
  • handler
  • page

Menggunakan page sebagai parameter rute dengan perutean atribut adalah kesalahan umum. Melakukan itu menghasilkan perilaku yang tidak konsisten dan membingungkan dengan pembuatan URL.

public class MyDemo2Controller : Controller
{
    [Route("/articles/{page}")]
    public IActionResult ListArticles(int page)
    {
        return ControllerContext.MyDisplayRouteInfo(page);
    }
}

Nama parameter khusus digunakan oleh pembuatan URL untuk menentukan apakah operasi pembuatan URL mengacu pada Razor Halaman atau ke Pengontrol.

Kata kunci berikut dicadangkan dalam konteks Razor tampilan atau Razor Halaman:

  • page
  • using
  • namespace
  • inject
  • section
  • inherits
  • model
  • addTagHelper
  • removeTagHelper

Kata kunci ini tidak boleh digunakan untuk pembuatan tautan, parameter terikat model, atau properti tingkat atas.

Templat kata kerja HTTP

ASP.NET Core memiliki templat kata kerja HTTP berikut:

Templat rute

ASP.NET Core memiliki templat rute berikut:

Perutean atribut dengan atribut kata kerja Http

Pertimbangkan pengontrol berikut:

[Route("api/[controller]")]
[ApiController]
public class Test2Controller : ControllerBase
{
    [HttpGet]   // GET /api/test2
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int/{id:int}")] // GET /api/test2/int/3
    public IActionResult GetIntProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
    public IActionResult GetInt2Product(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Dalam kode sebelumnya:

  • Setiap tindakan berisi [HttpGet] atribut , yang membatasi pencocokan dengan permintaan HTTP GET saja.
  • Tindakan ini GetProduct mencakup "{id}" templat, oleh karena itu id ditambahkan ke "api/[controller]" templat pada pengontrol. Templat metodenya adalah "api/[controller]/{id}". Oleh karena itu tindakan ini hanya cocok dengan permintaan GET untuk formulir /api/test2/xyz,/api/test2/123,/api/test2/{any string}, dll.
    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }
    
  • Tindakan GetIntProduct berisi "int/{id:int}" 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. Ini berarti bahwa banyak operasi, misalnya, GET dan POST pada sumber daya logis yang sama menggunakan URL yang sama. Perutean atribut menyediakan tingkat kontrol yang diperlukan untuk merancang tata letak titik akhir publik API dengan hati-hati.

Karena rute atribut berlaku untuk tindakan tertentu, mudah untuk membuat parameter yang diperlukan sebagai bagian dari definisi templat rute. Dalam contoh berikut, id diperlukan sebagai bagian dari jalur URL:

[ApiController]
public class Products2ApiController : ControllerBase
{
    [HttpGet("/products2/{id}", Name = "Products_List")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Tindakan Products2ApiController.GetProduct(int) :

  • Dijalankan dengan jalur URL seperti /products2/3
  • Tidak dijalankan dengan jalur /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);
    }
}

Nama rute dapat digunakan 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 tidak terlalu berulang, atribut rute pada pengontrol dikombinasikan dengan atribut rute pada aksi individu. Templat rute apa pun yang ditentukan pada pengontrol telah ditambahkan ke templat rute pada tindakan. Menempatkan atribut rute pada pengontrol membuat semua aksi 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 diterapkan ke tindakan yang dimulai dengan / atau ~/ tidak digabungkan dengan templat rute yang diterapkan ke pengontrol. Contoh berikut cocok dengan sekumpulan jalur URL yang mirip dengan rute default.

[Route("Home")]
public class HomeController : Controller
{
    [Route("")]
    [Route("Index")]
    [Route("/")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [Route("About")]
    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Tabel berikut menjelaskan [Route] atribut dalam kode sebelumnya:

Atribut Menggabungkan dengan [Route("Home")] Menentukan templat rute
[Route("")] Ya "Home"
[Route("Index")] Ya "Home/Index"
[Route("/")] Tidak ""
[Route("About")] Ya "Home/About"

Urutan rute atribut

Perutean membangun pohon dan mencocokkan semua titik akhir secara bersamaan:

  • Entri rute tampak seakan-akan 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 kode sebelumnya akan melemparkan pengecualian yang mirip 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 sebelumnya adalah contoh atau desain perutean yang buruk. Ini digunakan untuk mengilustrasikan sifat Order.
  • Properti Order hanya menyelesaikan ambiguitas, templat tersebut tidak dapat dicocokkan. Akan lebih baik untuk menghapus template [Route("Home")].

Lihat Razor Rute halaman dan konvensi aplikasi: Urutan rute untuk informasi tentang urutan rute dengan Razor Halaman.

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

[Route("[controller]/[action]")]
public class Products0Controller : Controller
{
    [HttpGet]
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }


    [HttpGet("{id}")]
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Dalam kode sebelumnya:

[HttpGet]
public IActionResult List()
{
    return ControllerContext.MyDisplayRouteInfo();
}
  • Korek api /Products0/List
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
    return ControllerContext.MyDisplayRouteInfo(id);
}
  • Korek api /Products0/Edit/{id}

Penggantian token terjadi sebagai langkah terakhir membangun rute atribut. Contoh sebelumnya 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.

Rute atribut juga dapat dikombinasikan dengan warisan. Ini sangat kuat ketika digabungkan dengan penggantian token. Penggantian token juga berlaku untuk nama rute yang ditentukan oleh rute atribut. [Route("[controller]/[action]", Name="[controller]_[action]")]menghasilkan nama rute unik untuk setiap tindakan:

[ApiController]
[Route("api/[controller]/[action]", Name = "[controller]_[action]")]
public abstract class MyBase2Controller : ControllerBase
{
}

public class Products11Controller : MyBase2Controller
{
    [HttpGet]                      // /api/products11/list
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]             //    /api/products11/edit/3
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Untuk mencocokkan pemisah [ penggantian token harfiah atau ], keluarkan dengan mengulangi karakter ([[ atau ]]).

Menggunakan transformator parameter untuk menyesuaikan penggantian token

Penggantian token dapat disesuaikan menggunakan transformator parameter. Transformator parameter mengimplementasikan IOutboundParameterTransformer dan mengubah nilai parameter. Misalnya, transformator parameter kustom SlugifyParameterTransformer mengubah SubscriptionManagement nilai rute menjadi subscription-management:

using System.Text.RegularExpressions;

public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
    public string? TransformOutbound(object? value)
    {
        if (value == null) { return null; }

        return Regex.Replace(value.ToString()!,
                             "([a-z])([A-Z])",
                             "$1-$2",
                             RegexOptions.CultureInvariant,
                             TimeSpan.FromMilliseconds(100)).ToLowerInvariant();
    }
}

RouteTokenTransformerConvention adalah konvensi model aplikasi yang:

  • Menerapkan transformator parameter ke semua rute atribut dalam aplikasi.
  • Menyesuaikan nilai token rute atribut selama proses penggantian.
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();

Lihat dokumen web MDN tentang Slug untuk definisi Slug.

Peringatan

Saat menggunakan System.Text.RegularExpressions untuk memproses masukan yang tidak tepercaya, berikan batas waktu. Pengguna jahat dapat memberikan masukan ke RegularExpressions yang menyebabkan Penolakan Serangan Layanan. ASP.NET API kerangka kerja Core yang menggunakan RegularExpressions batas waktu.

Beberapa rute atribut

Perutean atribut mendukung penentuan beberapa rute yang mencapai tindakan yang sama. Penggunaan yang paling umum adalah meniru perilaku rute konvensional default seperti yang ditunjukkan dalam contoh berikut:

[Route("[controller]")]
public class Products13Controller : Controller
{
    [Route("")]     // Matches 'Products13'
    [Route("Index")] // Matches 'Products13/Index'
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

Menempatkan beberapa atribut rute pada pengontrol berarti masing-masing 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.

Lihat Referensi Templat Rute Route Template Reference untuk deskripsi terperinci tentang sintaks templat rute.

Atribut rute kustom menggunakan IRouteTemplateProvider

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

  • Mencari atribut pada kelas pengontrol dan metode tindakan saat aplikasi dimulai.
  • Menggunakan atribut yang 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. Konvensi:

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

Bagian ini menunjukkan contoh dasar penyesuaian perutean menggunakan model aplikasi. Kode berikut membuat rute kira-kira sejajar dengan struktur folder proyek.

public class NamespaceRoutingConvention : Attribute, IControllerModelConvention
{
    private readonly string _baseNamespace;

    public NamespaceRoutingConvention(string baseNamespace)
    {
        _baseNamespace = baseNamespace;
    }

    public void Apply(ControllerModel controller)
    {
        var hasRouteAttributes = controller.Selectors.Any(selector =>
                                                selector.AttributeRouteModel != null);
        if (hasRouteAttributes)
        {
            return;
        }

        var namespc = controller.ControllerType.Namespace;
        if (namespc == null)
            return;
        var template = new StringBuilder();
        template.Append(namespc, _baseNamespace.Length + 1,
                        namespc.Length - _baseNamespace.Length - 1);
        template.Replace('.', '/');
        template.Append("/[controller]/[action]/{id?}");

        foreach (var selector in controller.Selectors)
        {
            selector.AttributeRouteModel = new AttributeRouteModel()
            {
                Template = template.ToString()
            };
        }
    }
}

Kode berikut mencegah namespace konvensi diterapkan ke pengontrol yang dirutekan atribut:

public void Apply(ControllerModel controller)
{
    var hasRouteAttributes = controller.Selectors.Any(selector =>
                                            selector.AttributeRouteModel != null);
    if (hasRouteAttributes)
    {
        return;
    }

Misalnya, pengontrol berikut tidak menggunakan NamespaceRoutingConvention:

[Route("[controller]/[action]/{id?}")]
public class ManagersController : Controller
{
    // /managers/index
    public IActionResult Index()
    {
        var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
        return Content($"Index- template:{template}");
    }

    public IActionResult List(int? id)
    {
        var path = Request.Path.Value;
        return Content($"List- Path:{path}");
    }
}

Metode NamespaceRoutingConvention.Apply:

  • Tidak melakukan apa pun jika pengontrol 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 mencampur penggunaan perutean konvensional dan perutean atribut. Biasanya menggunakan rute konvensional untuk pengontrol yang melayani halaman HTML untuk browser, dan perutean atribut untuk pengontrol yang melayani REST API.

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.

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 Dikodekan
/ %2F
+

Parameter rute tidak selalu didekodekan URL. Masalah ini dapat diatasi di masa mendatang. Untuk informasi selengkapnya, lihat masalah GitHub ini;

Pembuatan URL dan nilai lingkungan

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 default, nilai variabel url adalah string jalur URL /UrlGeneration/Destination. Jalur URL ini dibuat dengan perutean 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 menggunakan Url.Action, nilai rute saat ini untuk controller dan action disediakan oleh runtime:

  • Nilai controller dan action merupakan bagian dari nilai sekitar 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.
  • Nilai yang diinginkan dari c dan d harus ditentukan 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).

Nilai rute tambahan apa pun yang tidak cocok dengan parameter rute dimasukkan ke dalam string kueri.

public IActionResult Index()
{
    var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
    return Content(url!);
}

Kode sebelumnya menghasilkan /Products/Buy/17?color=red.

Kode berikut menghasilkan URL absolut:

public IActionResult Index2()
{
    var url = Url.Action("Buy", "Products", new { id = 17 }, protocol: Request.Scheme);
    // Returns https://localhost:5001/Products/Buy/17
    return Content(url!);
}

Untuk membuat URL absolut, gunakan salah satu hal berikut:

  • 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. Lihat Tag Helper dalam formulir untuk informasi selengkapnya.

Dalam tampilan, IUrlHelper tersedia melalui properti Url untuk pembuatan URL ad-hoc apa pun yang tidak tercakup oleh yang di atas.

Pembuatan URL dalam Hasil Tindakan

Contoh sebelumnya yang telah diperlihatkan menggunakan IUrlHelper dalam pengontrol. Penggunaan yang paling umum dalam pengontrol adalah menghasilkan URL sebagai bagian dari hasil tindakan.

Kelas ControllerBase dasar dan Controller menyediakan metode kenyamanan untuk hasil tindakan yang mereferensikan tindakan lain. Salah satu penggunaan umumnya adalah mengalihkan setelah menerima input pengguna:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, Customer customer)
{
    if (ModelState.IsValid)
    {
        // Update DB with new details.
        ViewData["Message"] = $"Successful edit of customer {id}";
        return RedirectToAction("Index");
    }
    return View(customer);
}

Tindakan menghasilkan metode pabrik seperti RedirectToAction dan CreatedAtAction mengikuti pola yang mirip dengan metode pada IUrlHelper.

Kasus khusus untuk rute konvensional 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 menggunakan default rute, tetapi mengapa? Anda mungkin menebak nilai rute { controller = Home, action = Index } cukup untuk menghasilkan sebuah URL menggunakan 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.

Daerah

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 dihasilkan oleh nilai bawaan untuk area. Rute yang dibuat oleh MapAreaControllerRoute setara dengan yang berikut ini:

app.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
        defaults: new { area = "Blog" }, constraints: new { area = "Blog" });
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");

MapAreaControllerRoute membuat rute menggunakan nilai default dan batasan untuk area, dengan menggunakan nama area yang disediakan, yaitu Blog. Nilai default memastikan bahwa rute selalu menghasilkan { area = Blog, ... }, batasan memerlukan nilai { area = Blog, ... } untuk pembuatan URL.

Perutean konvensional bergantung pada pesanan. Secara umum, rute dengan area harus ditempatkan lebih awal karena lebih spesifik daripada rute tanpa area.

Menggunakan contoh sebelumnya, nilai-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. Pengontrol tanpa [Area] atribut bukan anggota area apa pun, dan tidak cocok ketika area nilai rute disediakan oleh perutean. Dalam contoh berikut, hanya pengontrol pertama yang tercantum yang dapat 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}");
        }
    }
}

Namespace setiap kontroler ditampilkan di sini untuk kelengkapan informasi. 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 pengembangan, atur tingkat log di appsettings.Development.json:

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

ASP.NET Pengontrol Core menggunakan middleware Perutean untuk mencocokkan URL permintaan masuk dan memetakannya ke tindakan. Templat rute:

  • Didefinisikan dalam kode atau atribut startup.
  • Menjelaskan bagaimana jalur URL dicocokkan dengan tindakan.
  • Digunakan untuk menghasilkan URL untuk tautan. Tautan yang dihasilkan biasanya dikembalikan sebagai respons.

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 peruteanan default yang ditambahkan di ASP.NET Core 3.0, yang disebut sebagai endpoint routing. 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 yang berikut ini saat menggunakan perutean konvensional:

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

Di dalam panggilan ke UseEndpoints, MapControllerRoute digunakan untuk membuat satu rute. Rute tunggal 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. Lihat Pengikatan Model untuk detail lebih lanjut.

  • {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. Lihat Referensi Templat Rute Route Template Reference untuk deskripsi terperinci tentang sintaks 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 dijalankan untuk jalur URL berikut:

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

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

Metode kenyamanan MapDefaultControllerRoute:

endpoints.MapDefaultControllerRoute();

Menggantikan:

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

Penting

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

Perutean konvensional

Perutean konvensional digunakan dengan pengontrol dan tampilan. Rute default :

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

Sebelumnya adalah contoh rute konvensional. Ini disebut perutean konvensional karena menetapkan konvensi untuk jalur URL:

  • Segmen jalur pertama, {controller=Home}, memetakan ke nama pengontrol.
  • Segmen kedua, {action=Index}, memetakan ke nama tindakan .
  • Segmen ketiga, {id?} digunakan untuk opsional id. Yang di ? dalam {id?} membuatnya opsional. id digunakan untuk memetakan ke 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.

Menggunakan perutean konvensional dengan rute default memungkinkan mengembangkan aplikasi tanpa 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.

Peringatan

id dalam kode sebelumnya didefinisikan sebagai opsional oleh templat rute. Tindakan dapat dijalankan tanpa ID opsional yang disediakan sebagai bagian dari URL. Umumnya, ketika id dihilangkan dari URL:

  • id diatur ke 0 berdasarkan pengikatan model.
  • 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 menjamin urutan dalam eksekusi fitur ekstensi, semua titik akhir diproses secara bersamaan.

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 disebut rute konvensional khusus karena:

Karena controller dan action tidak muncul di templat "blog/{*article}" rute 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.

Contoh sebelumnya:

  • blog rute memiliki prioritas lebih tinggi untuk pencocokan daripada rute default karena ditambahkan terlebih dahulu.
  • Adalah contoh perutean gaya Slug yang biasanya menyertakan nama artikel sebagai bagian dari URL.

Peringatan

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

  • Tentukan konsep yang disebut rute. UseRouting menambahkan pencocokan rute pada 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 oleh aplikasi. Ini dimaksudkan untuk menyederhanakan kasus di mana rute konvensional tumpang tindih. Menambahkan rute menggunakan MapControllerRoute, MapDefaultControllerRoute, dan MapAreaControllerRoute secara otomatis menetapkan nilai urutan ke titik akhir mereka berdasarkan urutan pemanggilannya. Kecocokan dari rute yang muncul sebelumnya memiliki prioritas yang lebih tinggi. Perutean konvensional bergantung pada pesanan. Secara umum, rute dengan area harus ditempatkan lebih awal karena lebih spesifik daripada rute tanpa area. Rute konvensional khusus dengan parameter rute catch-all seperti dapat membuat rute terlalu {*article}, yang berarti bahwa rute tersebut cocok dengan URL yang Anda maksudkan untuk dicocokkan dengan rute lain. Letakkan rute serakah nanti di tabel rute untuk mencegah kecocokan serakah.

Peringatan

Parameter catch-all mungkin 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 yang perlu diaktifkan untuk bug ini tersedia dalam .NET Core 3.1.301 atau SDK yang lebih baru. Kode berikut menetapkan sakelar internal yang memperbaiki bug ini:

public static void Main(string[] args)
{
   AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior", 
                         true);
   CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.

Mengatasi tindakan ambigu

Saat dua titik akhir cocok melalui perutean, perutean harus melakukan salah satu hal berikut:

  • Pilih kandidat terbaik.
  • Tampilkan pengecualian.

Contohnya:

    public class Products33Controller : Controller
    {
        public IActionResult Edit(int id)
        {
            return ControllerContext.MyDisplayRouteInfo(id);
        }

        [HttpPost]
        public IActionResult Edit(int id, Product product)
        {
            return ControllerContext.MyDisplayRouteInfo(id, product.name);
        }
    }
}

Pengontrol sebelumnya mendefinisikan dua tindakan yang cocok:

  • Jalur URL /Products33/Edit/17
  • Merutekan data { controller = Products33, action = Edit, id = 17 }.

Ini adalah pola umum untuk pengontrol MVC:

  • Edit(int) menampilkan formulir untuk mengedit produk.
  • Edit(int, Product) memproses formulir yang diposting.

Untuk mengatasi rute yang benar:

  • Edit(int, Product) dipilih ketika permintaan adalah 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, sebuah AmbiguousMatchException akan diberikan, yang mencantumkan beberapa titik akhir yang cocok.

Nama rute konvensional

String "blog" dan "default" dalam contoh berikut adalah nama rute konvensional:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
    endpoints.MapControllerRoute(name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
});

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

Tindakan 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 khusus saat menggunakan Pengontrol atau Razor Halaman.

  • action
  • area
  • controller
  • handler
  • page

Menggunakan page sebagai parameter rute dengan perutean atribut adalah kesalahan umum. Melakukan itu menghasilkan perilaku yang tidak konsisten dan membingungkan dengan pembuatan URL.

public class MyDemo2Controller : Controller
{
    [Route("/articles/{page}")]
    public IActionResult ListArticles(int page)
    {
        return ControllerContext.MyDisplayRouteInfo(page);
    }
}

Nama parameter khusus digunakan oleh pembuatan URL untuk menentukan apakah operasi pembuatan URL mengacu pada Razor Halaman atau ke Pengontrol.

Kata kunci berikut dicadangkan dalam konteks Razor tampilan atau Razor Halaman:

  • page
  • using
  • namespace
  • inject
  • section
  • inherits
  • model
  • addTagHelper
  • removeTagHelper

Kata kunci ini tidak boleh digunakan untuk pembuatan tautan, parameter terikat model, atau properti tingkat atas.

Templat kata kerja HTTP

ASP.NET Core memiliki templat kata kerja HTTP berikut:

Templat rute

ASP.NET Core memiliki templat rute berikut:

Perutean atribut dengan atribut kata kerja Http

Pertimbangkan pengontrol berikut:

[Route("api/[controller]")]
[ApiController]
public class Test2Controller : ControllerBase
{
    [HttpGet]   // GET /api/test2
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int/{id:int}")] // GET /api/test2/int/3
    public IActionResult GetIntProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
    public IActionResult GetInt2Product(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Dalam kode sebelumnya:

  • Setiap tindakan berisi [HttpGet] atribut , yang membatasi pencocokan dengan permintaan HTTP GET saja.
  • Tindakan ini GetProduct mencakup "{id}" templat, oleh karena itu id ditambahkan ke "api/[controller]" templat pada pengontrol. Templat metodenya adalah "api/[controller]/{id}". Oleh karena itu tindakan ini hanya cocok dengan permintaan GET untuk formulir /api/test2/xyz,/api/test2/123,/api/test2/{any string}, dll.
    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }
    
  • Tindakan GetIntProduct berisi "int/{id:int}" 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. Ini berarti bahwa banyak operasi, misalnya, GET dan POST pada sumber daya logis yang sama menggunakan URL yang sama. Perutean atribut menyediakan tingkat kontrol yang diperlukan untuk merancang tata letak titik akhir publik API dengan hati-hati.

Karena rute atribut berlaku untuk tindakan tertentu, mudah untuk membuat parameter yang diperlukan sebagai bagian dari definisi templat rute. Dalam contoh berikut, id diperlukan sebagai bagian dari jalur URL:

[ApiController]
public class Products2ApiController : ControllerBase
{
    [HttpGet("/products2/{id}", Name = "Products_List")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Tindakan Products2ApiController.GetProduct(int) :

  • Dijalankan dengan jalur URL seperti /products2/3
  • Tidak dijalankan dengan jalur /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);
    }
}

Nama rute dapat digunakan 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 tidak terlalu berulang, atribut rute pada pengontrol dikombinasikan dengan atribut rute pada aksi individu. Templat rute apa pun yang ditentukan pada pengontrol telah ditambahkan ke templat rute pada tindakan. Menempatkan atribut rute pada pengontrol membuat semua aksi 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 diterapkan ke tindakan yang dimulai dengan / atau ~/ tidak digabungkan dengan templat rute yang diterapkan ke pengontrol. Contoh berikut cocok dengan sekumpulan jalur URL yang mirip dengan rute default.

[Route("Home")]
public class HomeController : Controller
{
    [Route("")]
    [Route("Index")]
    [Route("/")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [Route("About")]
    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Tabel berikut menjelaskan [Route] atribut dalam kode sebelumnya:

Atribut Menggabungkan dengan [Route("Home")] Menentukan templat rute
[Route("")] Ya "Home"
[Route("Index")] Ya "Home/Index"
[Route("/")] Tidak ""
[Route("About")] Ya "Home/About"

Urutan rute atribut

Perutean membangun pohon dan mencocokkan semua titik akhir secara bersamaan:

  • Entri rute tampak seakan-akan 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 kode sebelumnya akan melemparkan pengecualian yang mirip 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 sebelumnya adalah contoh atau desain perutean yang buruk. Ini digunakan untuk mengilustrasikan sifat Order.
  • Properti Order hanya menyelesaikan ambiguitas, templat tersebut tidak dapat dicocokkan. Akan lebih baik untuk menghapus template [Route("Home")].

Lihat Razor Rute halaman dan konvensi aplikasi: Urutan rute untuk informasi tentang urutan rute dengan Razor Halaman.

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

[Route("[controller]/[action]")]
public class Products0Controller : Controller
{
    [HttpGet]
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }


    [HttpGet("{id}")]
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Dalam kode sebelumnya:

[HttpGet]
public IActionResult List()
{
    return ControllerContext.MyDisplayRouteInfo();
}
  • Korek api /Products0/List
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
    return ControllerContext.MyDisplayRouteInfo(id);
}
  • Korek api /Products0/Edit/{id}

Penggantian token terjadi sebagai langkah terakhir membangun rute atribut. Contoh sebelumnya 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.

Rute atribut juga dapat dikombinasikan dengan warisan. Ini sangat kuat ketika digabungkan dengan penggantian token. Penggantian token juga berlaku untuk nama rute yang ditentukan oleh rute atribut. [Route("[controller]/[action]", Name="[controller]_[action]")]menghasilkan nama rute unik untuk setiap tindakan:

[ApiController]
[Route("api/[controller]/[action]", Name = "[controller]_[action]")]
public abstract class MyBase2Controller : ControllerBase
{
}

public class Products11Controller : MyBase2Controller
{
    [HttpGet]                      // /api/products11/list
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]             //    /api/products11/edit/3
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Untuk mencocokkan pemisah [ penggantian token harfiah atau ], keluarkan dengan mengulangi karakter ([[ atau ]]).

Menggunakan transformator parameter untuk menyesuaikan penggantian token

Penggantian token dapat disesuaikan menggunakan transformator parameter. Transformator parameter mengimplementasikan IOutboundParameterTransformer dan mengubah nilai parameter. Misalnya, transformator parameter kustom SlugifyParameterTransformer mengubah SubscriptionManagement nilai rute menjadi subscription-management:

public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
    public string TransformOutbound(object value)
    {
        if (value == null) { return null; }

        return Regex.Replace(value.ToString(),
                             "([a-z])([A-Z])",
                             "$1-$2",
                             RegexOptions.CultureInvariant,
                             TimeSpan.FromMilliseconds(100)).ToLowerInvariant();
    }
}

RouteTokenTransformerConvention adalah konvensi model aplikasi yang:

  • Menerapkan transformator parameter ke semua rute atribut dalam aplikasi.
  • Menyesuaikan nilai token rute atribut selama proses penggantian.
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()));
    });
}

Lihat dokumen web MDN tentang Slug untuk definisi Slug.

Peringatan

Saat menggunakan System.Text.RegularExpressions untuk memproses masukan yang tidak tepercaya, berikan batas waktu. Pengguna jahat dapat memberikan masukan ke RegularExpressions yang menyebabkan Penolakan Serangan Layanan. ASP.NET API kerangka kerja Core yang menggunakan RegularExpressions batas waktu.

Beberapa rute atribut

Perutean atribut mendukung penentuan beberapa rute yang mencapai tindakan yang sama. Penggunaan yang paling umum adalah meniru perilaku rute konvensional default seperti yang ditunjukkan dalam contoh berikut:

[Route("[controller]")]
public class Products13Controller : Controller
{
    [Route("")]     // Matches 'Products13'
    [Route("Index")] // Matches 'Products13/Index'
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

Menempatkan beberapa atribut rute pada pengontrol berarti masing-masing 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.

Lihat Referensi Templat Rute Route Template Reference untuk deskripsi terperinci tentang sintaks templat rute.

Atribut rute kustom menggunakan IRouteTemplateProvider

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

  • Mencari atribut pada kelas pengontrol dan metode tindakan saat aplikasi dimulai.
  • Menggunakan atribut yang 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. Konvensi:

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

Bagian ini menunjukkan contoh dasar penyesuaian perutean menggunakan model aplikasi. Kode berikut membuat rute kira-kira sejajar dengan struktur folder proyek.

public class NamespaceRoutingConvention : Attribute, IControllerModelConvention
{
    private readonly string _baseNamespace;

    public NamespaceRoutingConvention(string baseNamespace)
    {
        _baseNamespace = baseNamespace;
    }

    public void Apply(ControllerModel controller)
    {
        var hasRouteAttributes = controller.Selectors.Any(selector =>
                                                selector.AttributeRouteModel != null);
        if (hasRouteAttributes)
        {
            return;
        }

        var namespc = controller.ControllerType.Namespace;
        if (namespc == null)
            return;
        var template = new StringBuilder();
        template.Append(namespc, _baseNamespace.Length + 1,
                        namespc.Length - _baseNamespace.Length - 1);
        template.Replace('.', '/');
        template.Append("/[controller]/[action]/{id?}");

        foreach (var selector in controller.Selectors)
        {
            selector.AttributeRouteModel = new AttributeRouteModel()
            {
                Template = template.ToString()
            };
        }
    }
}

Kode berikut mencegah namespace konvensi diterapkan ke pengontrol yang dirutekan atribut:

public void Apply(ControllerModel controller)
{
    var hasRouteAttributes = controller.Selectors.Any(selector =>
                                            selector.AttributeRouteModel != null);
    if (hasRouteAttributes)
    {
        return;
    }

Misalnya, pengontrol berikut tidak menggunakan NamespaceRoutingConvention:

[Route("[controller]/[action]/{id?}")]
public class ManagersController : Controller
{
    // /managers/index
    public IActionResult Index()
    {
        var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
        return Content($"Index- template:{template}");
    }

    public IActionResult List(int? id)
    {
        var path = Request.Path.Value;
        return Content($"List- Path:{path}");
    }
}

Metode NamespaceRoutingConvention.Apply:

  • Tidak melakukan apa pun jika pengontrol 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 mencampur penggunaan perutean konvensional dan perutean atribut. Biasanya menggunakan rute konvensional untuk pengontrol yang melayani halaman HTML untuk browser, dan perutean atribut untuk pengontrol yang melayani REST API.

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 lingkungan

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 default, nilai variabel url adalah string jalur URL /UrlGeneration/Destination. Jalur URL ini dibuat dengan perutean 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 menggunakan Url.Action, nilai rute saat ini untuk controller dan action disediakan oleh runtime:

  • Nilai controller dan action merupakan bagian dari nilai sekitar 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.
  • Nilai yang diinginkan dari c dan d harus ditentukan 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).

Nilai rute tambahan apa pun yang tidak cocok dengan parameter rute dimasukkan ke dalam string kueri.

public IActionResult Index()
{
    var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
    return Content(url);
}

Kode sebelumnya menghasilkan /Products/Buy/17?color=red.

Kode berikut menghasilkan URL absolut:

public IActionResult Index2()
{
    var url = Url.Action("Buy", "Products", new { id = 17 }, protocol: Request.Scheme);
    // Returns https://localhost:5001/Products/Buy/17
    return Content(url);
}

Untuk membuat URL absolut, gunakan salah satu hal berikut:

  • 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. Lihat Tag Helper dalam formulir untuk informasi selengkapnya.

Dalam tampilan, IUrlHelper tersedia melalui properti Url untuk pembuatan URL ad-hoc apa pun yang tidak tercakup oleh yang di atas.

Pembuatan URL dalam Hasil Tindakan

Contoh sebelumnya yang telah diperlihatkan menggunakan IUrlHelper dalam pengontrol. Penggunaan yang paling umum dalam pengontrol adalah menghasilkan URL sebagai bagian dari hasil tindakan.

Kelas ControllerBase dasar dan Controller menyediakan metode kenyamanan untuk hasil tindakan yang mereferensikan tindakan lain. Salah satu penggunaan umumnya adalah mengalihkan setelah menerima input pengguna:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, Customer customer)
{
    if (ModelState.IsValid)
    {
        // Update DB with new details.
        ViewData["Message"] = $"Successful edit of customer {id}";
        return RedirectToAction("Index");
    }
    return View(customer);
}

Tindakan menghasilkan metode pabrik seperti RedirectToAction dan CreatedAtAction mengikuti pola yang mirip dengan metode pada IUrlHelper.

Kasus khusus untuk rute konvensional 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 menggunakan default rute, tetapi mengapa? Anda mungkin menebak nilai rute { controller = Home, action = Index } cukup untuk menghasilkan sebuah URL menggunakan 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.

Daerah

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 dihasilkan oleh nilai bawaan untuk area. Rute yang dibuat oleh MapAreaControllerRoute setara dengan yang berikut ini:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
        defaults: new { area = "Blog" }, constraints: new { area = "Blog" });
    endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});

MapAreaControllerRoute membuat rute menggunakan nilai default dan batasan untuk area, dengan menggunakan nama area yang disediakan, yaitu Blog. Nilai default memastikan bahwa rute selalu menghasilkan { area = Blog, ... }, batasan memerlukan nilai { area = Blog, ... } untuk pembuatan URL.

Perutean konvensional bergantung pada pesanan. Secara umum, rute dengan area harus ditempatkan lebih awal karena lebih spesifik daripada rute tanpa area.

Menggunakan contoh sebelumnya, nilai-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. Pengontrol tanpa [Area] atribut bukan anggota area apa pun, dan tidak cocok ketika area nilai rute disediakan oleh perutean. Dalam contoh berikut, hanya pengontrol pertama yang tercantum yang dapat 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}");
        }
    }
}

Namespace setiap kontroler ditampilkan di sini untuk kelengkapan informasi. 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 pengembangan, atur tingkat log di appsettings.Development.json:

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