Perutean ke tindakan pengontrol di ASP.NET Core

Oleh Ryan Nowak, Kirk Larkin, dan Rick Anderson

Catatan

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

Penting

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

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

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

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

Tindakan dirutekan secara konvensional atau dirutekan atribut. Menempatkan rute pada pengontrol atau tindakan membuatnya dirutekan atribut. Lihat Perutean campuran untuk informasi selengkapnya.

Dokumen ini:

  • Menjelaskan interaksi antara MVC dan perutean:
    • Bagaimana aplikasi MVC khas memanfaatkan fitur perutean.
    • Mencakup keduanya:
      • Perutean konvensional biasanya digunakan dengan pengontrol dan tampilan.
      • Perutean atribut yang digunakan dengan REST API. Jika Anda terutama tertarik pada perutean untuk REST API, lompat ke bagian Perutean atribut untuk REST API .
    • Lihat Perutean untuk detail perutean tingkat lanjut.
  • Mengacu pada sistem perutean default yang disebut perutean titik akhir. Dimungkinkan untuk menggunakan pengontrol dengan versi perutean sebelumnya untuk tujuan kompatibilitas. Lihat panduan migrasi 2.2-3.0 untuk petunjuknya.

Menyiapkan rute konvensional

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

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

var app = builder.Build();

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

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

app.UseRouting();

app.UseAuthorization();

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

app.Run();

MapControllerRoute digunakan untuk membuat satu rute. Rute tunggal diberi nama default rute. Sebagian besar aplikasi dengan pengontrol dan tampilan menggunakan templat rute yang mirip default dengan rute. REST API harus menggunakan perutean atribut.

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

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

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

  • Mengekstrak nilai { controller = Products, action = Details, id = 5 } rute dengan membuat token jalur. Ekstraksi nilai rute menghasilkan kecocokan jika aplikasi memiliki pengontrol 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 selengkapnya.

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

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

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

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

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

Nilai untuk controller dan action memanfaatkan nilai default. id tidak menghasilkan nilai karena tidak ada segmen yang sesuai di jalur URL. / hanya cocok jika ada HomeController tindakan dan Index :

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

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

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

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

Metode MapDefaultControllerRoutekenyamanan :

app.MapDefaultControllerRoute();

Menggantikan:

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

Penting

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

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. in ?{id?} membuatnya opsional. id digunakan untuk memetakan ke entitas model.

Dengan menggunakan rute ini default , jalur URL:

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

Pemetaan ini:

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

Menggunakan perutean konvensional dengan rute default memungkinkan pembuatan aplikasi tanpa harus membuat pola URL baru untuk setiap tindakan. Untuk aplikasi dengan tindakan gaya CRUD , memiliki konsistensi untuk URL di seluruh pengontrol:

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

Peringatan

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

  • id diatur ke 0 berdasarkan pengikatan model.
  • Tidak ada entitas yang ditemukan dalam database yang cocok id == 0.

Perutean atribut memberikan kontrol menenangkan untuk membuat ID diperlukan untuk beberapa tindakan dan bukan untuk yang lain. Menurut konvensi, dokumentasi menyertakan parameter opsional seperti id saat kemungkinan akan muncul dalam penggunaan yang benar.

Sebagian besar aplikasi harus memilih skema perutean dasar dan deskriptif sehingga URL dapat dibaca dan bermakna. Rute {controller=Home}/{action=Index}/{id?}konvensional default :

  • Mendukung skema perutean dasar dan deskriptif.
  • Adalah titik awal yang berguna untuk aplikasi berbasis UI.
  • Adalah satu-satunya templat rute yang diperlukan untuk banyak aplikasi UI web. Untuk aplikasi UI web yang lebih besar, rute lain yang menggunakan Area sering kali merupakan semua yang diperlukan.

MapControllerRoute dan MapAreaRoute :

  • Secara otomatis menetapkan nilai pesanan ke titik akhir mereka berdasarkan urutan yang dipanggil.

Perutean titik akhir di ASP.NET Core:

  • Tidak memiliki konsep rute.
  • Tidak memberikan jaminan pemesanan untuk eksekusi ekstensibilitas, semua titik akhir diproses sekaligus.

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

Perutean atribut dijelaskan nanti dalam dokumen ini.

Beberapa rute konvensional

Beberapa rute konvensional dapat dikonfigurasi dengan menambahkan lebih banyak panggilan ke MapControllerRoute dan MapAreaControllerRoute. Melakukannya memungkinkan menentukan beberapa konvensi, atau menambahkan rute konvensional yang didedikasikan untuk tindakan tertentu, seperti:

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

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

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

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

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

Contoh sebelumnya:

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

Peringatan

Di ASP.NET Core, perutean tidak:

  • Tentukan konsep yang disebut rute. UseRouting menambahkan pencocokan rute ke alur middleware. 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 Perutean untuk bahan referensi tentang perutean.

Urutan perutean konvensional

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

Peringatan

Parameter catch-all mungkin salah mencocokkan rute karena bug dalam perutean. Aplikasi yang terpengaruh oleh bug ini memiliki karakteristik berikut:

  • Rute catch-all, misalnya, {**slug}"
  • Rute catch-all gagal mencocokkan permintaan yang harus cocok.
  • Menghapus rute lain membuat rute catch-all mulai berfungsi.

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

Perbaikan keikutsertaan untuk bug ini terkandung dalam .NET Core 3.1.301 SDK dan yang lebih baru. Kode berikut menetapkan sakelar internal yang memperbaiki bug ini:

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

Mengatasi tindakan ambigu

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

  • Pilih kandidat terbaik.
  • Tampilkan pengecualian.

Contohnya:

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

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

Pengontrol sebelumnya mendefinisikan dua tindakan yang cocok:

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

Ini adalah pola umum untuk pengontrol MVC:

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

Untuk mengatasi rute yang benar:

  • Edit(int, Product) dipilih ketika permintaan adalah 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. Edit(int, Product) membuat HttpPostAttribute kecocokan yang lebih baik daripada Edit(int).

Penting untuk memahami peran atribut seperti HttpPostAttribute. Atribut serupa didefinisikan untuk kata kerja HTTP lainnya. Dalam perutean konvensional, umum bagi tindakan untuk menggunakan nama tindakan yang sama saat mereka menjadi bagian dari formulir acara, kirim alur kerja formulir. Misalnya, lihat Memeriksa dua metode tindakan Edit.

Jika perutean tidak dapat memilih kandidat terbaik, sebuah AmbiguousMatchException dilemparkan, mencantumkan beberapa titik akhir yang cocok.

Nama rute konvensional

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

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

Nama rute memberi rute nama logis. Rute bernama dapat digunakan untuk pembuatan URL. Menggunakan rute bernama menyederhanakan pembuatan URL saat pengurutan rute dapat membuat pembuatan URL menjadi rumit. Nama rute harus lebar aplikasi yang unik.

Nama rute:

  • Tidak berdampak pada pencocokan URL atau penanganan permintaan.
  • Hanya digunakan untuk pembuatan URL.

Konsep nama rute diwakili dalam perutean sebagai IEndpointNameMetadata. Nama rute istilah dan nama titik akhir:

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

Perutean atribut untuk REST API

REST API harus menggunakan perutean atribut untuk memodelkan fungsionalitas aplikasi sebagai sekumpulan sumber daya di mana operasi diwakili oleh kata kerja HTTP.

Perutean atribut menggunakan sekumpulan atribut untuk memetakan tindakan langsung ke templat rute. Kode berikut khas untuk REST API dan digunakan dalam sampel berikutnya:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Dalam kode sebelumnya, MapControllers dipanggil untuk memetakan pengontrol rute atribut.

Dalam contoh berikut:

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

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

Tindakan HomeController.Index dijalankan untuk salah satu jalur /URL , , /Home, /Home/Indexatau /Home/Index/3.

Contoh ini menyoroti perbedaan pemrograman utama antara perutean atribut dan perutean konvensional. Perutean atribut memerlukan lebih banyak input untuk menentukan rute. Rute default konvensional menangani rute dengan lebih tepat. Namun, perutean atribut memungkinkan dan memerlukan kontrol yang tepat tentang templat rute mana yang berlaku untuk setiap tindakan.

Dengan perutean atribut, pengontrol dan nama tindakan tidak memainkan bagian di mana tindakan dicocokkan, kecuali penggantian token digunakan. Contoh berikut cocok dengan URL yang sama dengan contoh sebelumnya:

public class MyDemoController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult MyIndex(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

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

Kode berikut menggunakan penggantian token untuk action dan controller:

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

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

Kode berikut berlaku [Route("[controller]/[action]")] untuk pengontrol:

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

    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Dalam kode sebelumnya, Index templat metode harus didahului / atau ~/ ke templat rute. Templat rute diterapkan ke tindakan yang dimulai dengan / atau ~/ tidak digabungkan dengan templat rute yang diterapkan ke pengontrol.

Lihat Prioritas templat rute untuk informasi tentang pemilihan templat rute.

Nama perutean yang dicadangkan

Kata kunci berikut adalah nama parameter rute yang dipesan saat menggunakan Pengontrol atau Razor Halaman:

  • action
  • area
  • controller
  • handler
  • page

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

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

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

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

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

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

Templat kata kerja HTTP

ASP.NET Core memiliki templat kata kerja HTTP berikut:

Templat rute

ASP.NET Core memiliki templat rute berikut:

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}" templat. Bagian :int templat membatasi id nilai rute ke string yang dapat dikonversi menjadi bilangan bulat. Permintaan GET ke /api/test2/int/abc:
    • Tidak cocok dengan tindakan ini.
    • Mengembalikan kesalahan 404 Tidak Ditemukan .
      [HttpGet("int/{id:int}")] // GET /api/test2/int/3
      public IActionResult GetIntProduct(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      
  • Tindakan GetInt2Product berisi {id} dalam templat, tetapi tidak 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 Permintaan Buruk 400 karena pengikatan model gagal dikonversi abc ke bilangan bulat.
      [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
      public IActionResult GetInt2Product(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      

Perutean atribut dapat menggunakan HttpMethodAttribute atribut seperti HttpPostAttribute, , HttpPutAttributedan HttpDeleteAttribute. Semua atribut kata kerja HTTP menerima templat rute. Contoh berikut menunjukkan dua tindakan yang cocok dengan templat rute yang sama:

[ApiController]
public class MyProductsController : ControllerBase
{
    [HttpGet("/products3")]
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpPost("/products3")]
    public IActionResult CreateProduct(MyProduct myProduct)
    {
        return ControllerContext.MyDisplayRouteInfo(myProduct.Name);
    }
}

Menggunakan jalur /products3URL :

  • Tindakan MyProductsController.ListProducts berjalan ketika kata kerja HTTP adalah GET.
  • Tindakan MyProductsController.CreateProduct berjalan ketika kata kerja 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 tepat tentang apa yang didukung API Anda. REST Klien API diharapkan mengetahui jalur dan kata kerja HTTP apa yang dipetakan ke operasi logis tertentu.

REST API harus menggunakan perutean atribut untuk memodelkan fungsionalitas aplikasi sebagai sekumpulan sumber daya di mana operasi diwakili oleh kata kerja HTTP. Ini berarti bahwa banyak operasi, misalnya, GET dan POST pada sumber daya logis yang sama menggunakan URL yang sama. Perutean atribut menyediakan tingkat kontrol yang diperlukan untuk merancang tata letak titik akhir publik API dengan hati-hati.

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

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

Tindakan Products2ApiController.GetProduct(int) :

  • Dijalankan dengan jalur URL seperti /products2/3
  • Tidak dijalankan dengan jalur /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 Konsumsi.

Lihat Perutean untuk deskripsi lengkap templat rute dan opsi terkait.

Untuk informasi selengkapnya tentang [ApiController], lihat atribut ApiController.

Nama rute

Kode berikut mendefinisikan nama rute :Products_List

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

Nama rute dapat digunakan untuk menghasilkan URL berdasarkan rute tertentu. Nama rute:

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

Nama rute harus unik di seluruh aplikasi.

Berbeda dengan kode sebelumnya dengan rute default konvensional, yang mendefinisikan id parameter sebagai opsional ({id?}). Kemampuan untuk menentukan API dengan tepat memiliki keuntungan, seperti mengizinkan /products dan /products/5 dikirim ke tindakan yang berbeda.

Menggabungkan rute atribut

Untuk membuat perutean atribut kurang berulang, atribut rute pada pengontrol dikombinasikan dengan atribut rute pada tindakan individual. Templat rute apa pun yang ditentukan pada pengontrol telah ditambahkan ke templat rute pada tindakan. Menempatkan atribut rute pada pengontrol membuat semua tindakan di pengontrol menggunakan perutean atribut.

[ApiController]
[Route("products")]
public class ProductsApiController : ControllerBase
{
    [HttpGet]
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

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

Dalam contoh sebelumnya:

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

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

Templat rute diterapkan ke tindakan yang dimulai dengan / atau ~/ tidak digabungkan dengan templat rute yang diterapkan ke pengontrol. Contoh berikut cocok dengan sekumpulan jalur URL yang mirip dengan rute default.

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

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

Tabel berikut menjelaskan [Route] atribut dalam kode sebelumnya:

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

Urutan rute atribut

Perutean membangun pohon dan mencocokkan semua titik akhir secara bersamaan:

  • Entri rute berulah seolah-olah ditempatkan dalam urutan yang ideal.
  • Rute yang paling spesifik memiliki kesempatan untuk dijalankan sebelum rute yang lebih umum.

Misalnya, rute atribut seperti blog/search/{topic} lebih spesifik daripada rute atribut seperti blog/{*article}. Rute blog/search/{topic} memiliki prioritas yang lebih tinggi, secara default, karena lebih spesifik. Dengan menggunakan perutean konvensional, pengembang bertanggung jawab untuk menempatkan rute dalam urutan yang diinginkan.

Rute atribut dapat mengonfigurasi pesanan Order menggunakan properti . Semua atribut rute yang disediakan kerangka kerja meliputi Order . Rute diproses sesuai dengan jenis properti yang Order naik. Urutan defaultnya adalah 0. Mengatur rute menggunakan Order = -1 eksekusi sebelum rute yang tidak mengatur pesanan. Mengatur rute menggunakan Order = 1 eksekusi setelah pemesanan rute default.

Hindari tergantung pada Order. Jika ruang URL aplikasi memerlukan nilai pesanan eksplisit untuk merutekan dengan benar, kemungkinan membingungkan klien juga. Secara umum, perutean atribut memilih rute yang benar dengan pencocokan URL. Jika urutan default yang digunakan untuk pembuatan URL tidak berfungsi, menggunakan nama rute sebagai penimpaan biasanya lebih sederhana daripada menerapkan Order properti.

Pertimbangkan dua pengontrol berikut yang keduanya menentukan pencocokan /homerute :

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

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult About(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}
public class MyDemoController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult MyIndex(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

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

/home Meminta dengan kode sebelumnya melemparkan pengecualian yang mirip dengan yang berikut ini:

AmbiguousMatchException: The request matched multiple endpoints. Matches:

 WebMvcRouting.Controllers.HomeController.Index
 WebMvcRouting.Controllers.MyDemoController.MyIndex

Order Menambahkan ke salah satu atribut rute menyelesaikan ambiguitas:

[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
    return ControllerContext.MyDisplayRouteInfo();
}

Dengan kode sebelumnya, /home menjalankan HomeController.Index titik akhir. Untuk sampai ke MyDemoController.MyIndexpermintaan , /home/MyIndex. Catatan:

  • Kode sebelumnya adalah contoh atau desain perutean yang buruk. Ini digunakan untuk mengilustrasikan Order properti .
  • Properti Order hanya menyelesaikan ambiguitas, templat tersebut tidak dapat dicocokkan. Akan lebih baik untuk menghapus [Route("Home")] templat.

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

Dalam beberapa kasus, kesalahan HTTP 500 dikembalikan dengan rute ambigu. Gunakan pengelogan untuk melihat titik akhir mana yang menyebabkan AmbiguousMatchException.

Penggantian token dalam templat rute [pengontrol], [tindakan], [area]

Untuk kenyamanan, rute atribut mendukung penggantian token dengan menyertakan token dalam kurung siku ([, ]). Token [action], [area], dan [controller] diganti dengan nilai nama tindakan, nama area, dan nama pengontrol dari tindakan tempat rute ditentukan:

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


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

Dalam kode sebelumnya:

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

Penggantian token terjadi sebagai langkah terakhir membangun rute atribut. Contoh sebelumnya berpura-pura sama dengan kode berikut:

public class Products20Controller : Controller
{
    [HttpGet("[controller]/[action]")]  // Matches '/Products20/List'
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("[controller]/[action]/{id}")]   // Matches '/Products20/Edit/{id}'
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Jika Anda membaca ini dalam bahasa selain bahasa Inggris, beri tahu kami dalam masalah diskusi GitHub ini jika Anda ingin melihat komentar kode dalam bahasa asli Anda.

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

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

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

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

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

Menggunakan transformator parameter untuk menyesuaikan penggantian token

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

using System.Text.RegularExpressions;

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

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

RouteTokenTransformerConvention adalah konvensi model aplikasi yang:

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

Metode sebelumnya ListAll cocok /subscription-management/list-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 di Slug untuk definisi Slug.

Peringatan

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

Beberapa rute atribut

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

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

Menempatkan beberapa atribut rute pada pengontrol berarti masing-masing menggabungkan dengan masing-masing atribut rute pada metode tindakan:

[Route("Store")]
[Route("[controller]")]
public class Products6Controller : Controller
{
    [HttpPost("Buy")]       // Matches 'Products6/Buy' and 'Store/Buy'
    [HttpPost("Checkout")]  // Matches 'Products6/Checkout' and 'Store/Checkout'
    public IActionResult Buy()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Semua batasan rute kata kerja HTTP menerapkan IActionConstraint.

Saat beberapa atribut rute yang diterapkan IActionConstraint ditempatkan pada tindakan:

  • Setiap batasan tindakan dikombinasikan dengan templat rute yang diterapkan ke pengontrol.
[Route("api/[controller]")]
public class Products7Controller : ControllerBase
{
    [HttpPut("Buy")]        // Matches PUT 'api/Products7/Buy'
    [HttpPost("Checkout")]  // Matches POST 'api/Products7/Checkout'
    public IActionResult Buy()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Menggunakan beberapa rute pada tindakan mungkin tampak berguna dan kuat, lebih baik menjaga ruang URL aplikasi Anda tetap dasar dan terdefinisi dengan baik. Gunakan beberapa rute pada tindakan hanya jika diperlukan, misalnya, untuk mendukung klien yang ada.

Menentukan parameter opsional rute atribut, nilai default, dan batasan

Rute atribut mendukung sintaksis sebaris yang sama dengan rute konvensional untuk menentukan parameter opsional, nilai default, dan batasan.

public class Products14Controller : Controller
{
    [HttpPost("product14/{id:int}")]
    public IActionResult ShowProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Dalam kode sebelumnya, [HttpPost("product14/{id:int}")] menerapkan batasan rute. Tindakan Products14Controller.ShowProduct hanya dicocokkan dengan jalur URL seperti /product14/3. Bagian {id:int} templat rute membatasi segmen tersebut hanya untuk bilangan bulat.

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

Atribut rute kustom menggunakan IRouteTemplateProvider

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

  • Mencari atribut pada kelas pengontrol dan metode tindakan saat aplikasi dimulai.
  • Menggunakan atribut yang diterapkan IRouteTemplateProvider untuk membangun serangkaian rute awal.

Terapkan IRouteTemplateProvider untuk menentukan atribut rute kustom. Masing-masing IRouteTemplateProvider memungkinkan Anda menentukan satu rute dengan templat, pesanan, dan nama rute kustom:

public class MyApiControllerAttribute : Attribute, IRouteTemplateProvider
{
    public string Template => "api/[controller]";
    public int? Order => 2;
    public string Name { get; set; } = string.Empty;
}

[MyApiController]
[ApiController]
public class MyTestApiController : ControllerBase
{
    // GET /api/MyTestApi
    [HttpGet]
    public IActionResult Get()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Metode sebelumnya Get mengembalikan Order = 2, Template = api/MyTestApi.

Menggunakan model aplikasi untuk menyesuaikan rute atribut

Model aplikasi:

  • Adalah model objek yang dibuat saat startup di Program.cs.
  • Berisi semua metadata yang digunakan oleh ASP.NET Core untuk merutekan dan menjalankan tindakan dalam aplikasi.

Model aplikasi mencakup semua data yang dikumpulkan dari atribut rute. Data dari atribut rute disediakan oleh IRouteTemplateProvider implementasi. Konvensi:

  • Dapat ditulis untuk mengubah model aplikasi untuk menyesuaikan perilaku perutean.
  • Dibaca saat pengaktifan aplikasi.

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

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

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

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

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

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

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

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

Misalnya, pengontrol berikut tidak menggunakan NamespaceRoutingConvention:

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

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

Metode NamespaceRoutingConvention.Apply:

  • Tidak melakukan apa pun jika pengontrol dirutekan atributnya.
  • Mengatur templat pengontrol berdasarkan namespace, dengan 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:

  • Basisnya namespace adalah My.Application.
  • Nama lengkap pengontrol sebelumnya adalah My.Application.Admin.Controllers.UsersController.
  • Mengatur NamespaceRoutingConvention templat pengontrol 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.

Tindakan dirutekan secara konvensional atau atribut yang dirutekan. Menempatkan rute pada pengontrol atau tindakan membuatnya atribut dirutekan. Tindakan yang menentukan rute atribut tidak dapat dicapai melalui rute konvensional dan sebaliknya. Atribut rute apa pun pada pengontrol membuat semua tindakan dalam atribut pengontrol dirutekan.

Perutean atribut dan perutean konvensional menggunakan mesin perutean yang sama.

Perutean dengan karakter khusus

Perutean dengan karakter khusus dapat menyebabkan hasil yang tidak terduga. Misalnya, pertimbangkan pengontrol dengan metode tindakan berikut:

[HttpGet("{id?}/name")]
public async Task<ActionResult<string>> GetName(string id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null || todoItem.Name == null)
    {
        return NotFound();
    }

    return todoItem.Name;
}

Ketika string id berisi nilai yang dikodekan berikut, hasil yang tidak terduga mungkin terjadi:

ASCII Dikodekan
/ %2F
+

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

Pembuatan URL dan nilai sekitar

Aplikasi dapat menggunakan fitur pembuatan URL perutean untuk menghasilkan tautan URL ke tindakan. Menghasilkan URL menghilangkan URL pengodean keras, membuat kode lebih kuat dan dapat dipertahankan. Bagian ini berfokus pada fitur pembuatan URL yang disediakan oleh MVC dan hanya mencakup dasar-dasar cara kerja pembuatan URL. Lihat Perutean untuk deskripsi terperinci tentang pembuatan URL.

Antarmuka IUrlHelper adalah elemen infrastruktur yang mendasar antara MVC dan perutean untuk pembuatan URL. Instans IUrlHelper tersedia melalui Url properti di pengontrol, tampilan, dan komponen tampilan.

Dalam contoh berikut, IUrlHelper antarmuka digunakan melalui Controller.Url properti untuk menghasilkan URL ke tindakan lain.

public class UrlGenerationController : Controller
{
    public IActionResult Source()
    {
        // Generates /UrlGeneration/Destination
        var url = Url.Action("Destination");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    public IActionResult Destination()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Jika aplikasi menggunakan rute konvensional default, nilai url variabel adalah string /UrlGeneration/Destinationjalur URL . Jalur URL ini dibuat dengan perutean dengan menggabungkan:

  • Nilai rute dari permintaan saat ini, yang disebut nilai sekitar.
  • Nilai yang diteruskan ke Url.Action dan mengganti nilai tersebut ke dalam templat rute:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}

result: /UrlGeneration/Destination

Setiap parameter rute dalam templat rute memiliki nilainya digantikan dengan mencocokkan nama dengan nilai dan nilai sekitar. Parameter rute yang tidak memiliki nilai dapat:

  • Gunakan nilai default jika memilikinya.
  • Dilewati jika bersifat opsional. Misalnya, id dari templat {controller}/{action}/{id?}rute .

Pembuatan URL gagal jika parameter rute yang diperlukan tidak memiliki nilai yang sesuai. Jika pembuatan URL gagal untuk rute, rute berikutnya dicoba hingga semua rute telah dicoba atau kecocokan ditemukan.

Contoh Url.Action sebelumnya mengasumsikan perutean konvensional. Pembuatan URL berfungsi sama dengan perutean atribut, meskipun konsepnya berbeda. Dengan perutean konvensional:

  • Nilai rute digunakan untuk memperluas templat.
  • Nilai rute untuk controller dan action biasanya muncul dalam templat tersebut. Ini berfungsi karena URL yang cocok dengan perutean mematuhi konvensi.

Contoh berikut menggunakan perutean atribut:

public class UrlGenerationAttrController : Controller
{
    [HttpGet("custom")]
    public IActionResult Source()
    {
        var url = Url.Action("Destination");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    [HttpGet("custom/url/to/destination")]
    public IActionResult Destination()
    {
       return ControllerContext.MyDisplayRouteInfo();
    }
}

Tindakan Source dalam kode sebelumnya menghasilkan custom/url/to/destination.

LinkGenerator ditambahkan di ASP.NET Core 3.0 sebagai alternatif untuk IUrlHelper. LinkGenerator menawarkan fungsionalitas yang serupa tetapi lebih fleksibel. Setiap metode pada IUrlHelper memiliki keluarga metode yang LinkGenerator sesuai juga.

Membuat URL menurut nama tindakan

Url.Action, LinkGenerator.GetPathByAction, dan semua kelebihan beban terkait semuanya dirancang untuk menghasilkan titik akhir target dengan menentukan nama pengontrol dan nama tindakan.

Saat menggunakan Url.Action, nilai rute saat ini untuk controller dan action disediakan oleh runtime:

  • Nilai controller dan action merupakan bagian dari nilai dan nilai sekitar. 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 pembuatan URL dan gagal.
  • Nilai yang diinginkan dan cd harus ditentukan untuk menghasilkan URL.

Anda mungkin mengharapkan untuk mengalami masalah ini dengan rute {controller}/{action}/{id?}default . Masalah ini jarang terjadi dalam praktiknya karena Url.Action selalu secara eksplisit menentukan nilai controller dan action .

Beberapa kelebihan beban Url.Action mengambil objek nilai rute untuk menyediakan nilai untuk parameter rute selain controller dan action. Objek nilai rute sering digunakan dengan id. Contohnya,Url.Action("Buy", "Products", new { id = 17 }). Objek nilai rute:

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

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

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

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

Kode berikut menghasilkan URL absolut:

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

Untuk membuat URL absolut, gunakan salah satu hal berikut:

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

Hasilkan URL menurut rute

Kode sebelumnya menunjukkan pembuatan URL dengan meneruskan pengontrol dan nama tindakan. IUrlHelper juga menyediakan keluarga metode Url.RouteUrl . Metode ini mirip dengan Url.Action, tetapi tidak menyalin nilai action saat ini dan controller ke nilai rute. Penggunaan yang paling umum dari Url.RouteUrl:

  • Menentukan nama rute untuk menghasilkan URL.
  • Umumnya tidak menentukan pengontrol atau nama tindakan.
public class UrlGeneration2Controller : Controller
{
    [HttpGet("")]
    public IActionResult Source()
    {
        var url = Url.RouteUrl("Destination_Route");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    [HttpGet("custom/url/to/destination2", Name = "Destination_Route")]
    public IActionResult Destination()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

File berikut Razor menghasilkan tautan HTML ke Destination_Route:

<h1>Test Links</h1>

<ul>
    <li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>

Hasilkan URL dalam HTML dan Razor

IHtmlHelperHtmlHelper menyediakan metode Html.BeginForm dan Html.ActionLink untuk menghasilkan <form> elemen dan <a> masing-masing. Metode ini menggunakan metode Url.Action untuk menghasilkan URL dan mereka menerima argumen serupa. Pendamping Url.RouteUrl untuk HtmlHelper adalah Html.BeginRouteForm dan Html.RouteLink yang memiliki fungsionalitas serupa.

TagHelpers menghasilkan URL melalui form TagHelper dan <a> TagHelper. Kedua penggunaan IUrlHelper ini untuk implementasinya. Lihat Tag Helper dalam formulir untuk informasi selengkapnya.

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

Pembuatan URL dalam Hasil Tindakan

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

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

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

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

Kasus khusus untuk rute konvensional khusus

Perutean konvensional dapat menggunakan jenis definisi rute khusus yang disebut rute konvensional khusus. Dalam contoh berikut, rute bernama blog adalah rute konvensional khusus:

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

Menggunakan definisi rute sebelumnya, Url.Action("Index", "Home") menghasilkan jalur / URL menggunakan default rute, tetapi mengapa? Anda mungkin menebak nilai { controller = Home, action = Index } rute akan cukup untuk menghasilkan URL menggunakan blog, dan hasilnya adalah /blog?action=Index&controller=Home.

Rute konvensional khusus mengandalkan perilaku khusus nilai default yang tidak memiliki parameter rute yang sesuai yang mencegah rute terlalu serakah dengan pembuatan URL. Dalam hal ini nilai defaultnya adalah { controller = Blog, action = Article }, dan tidak controller juga action muncul sebagai parameter rute. Saat perutean melakukan pembuatan URL, nilai yang disediakan harus cocok dengan nilai default. Pembuatan URL menggunakan blog gagal karena nilainya { controller = Home, action = Index } tidak cocok { controller = Blog, action = Article }. Perutean kemudian kembali untuk mencoba default, yang berhasil.

Daerah

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

  • Perutean namespace layanan untuk tindakan pengontrol.
  • Struktur folder untuk tampilan.

Menggunakan area memungkinkan aplikasi memiliki beberapa pengontrol dengan nama yang sama, selama memiliki area yang berbeda. Menggunakan area membuat hierarki untuk tujuan perutean dengan menambahkan parameter rute lain, area ke controller dan action. Bagian ini membahas bagaimana perutean berinteraksi dengan area. Lihat Area untuk detail tentang bagaimana area digunakan dengan tampilan.

Contoh berikut mengonfigurasi MVC untuk menggunakan rute konvensional default dan area rute untuk yang area bernama Blog:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

var app = builder.Build();

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

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

app.UseRouting();

app.UseAuthorization();

app.MapAreaControllerRoute("blog_route", "Blog",
        "Manage/{controller}/{action}/{id?}");
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");

app.Run();

Dalam kode sebelumnya, MapAreaControllerRoute dipanggil untuk membuat "blog_route". Parameter kedua, "Blog", adalah nama area.

Saat mencocokkan jalur URL seperti /Manage/Users/AddUser, "blog_route" rute menghasilkan nilai { area = Blog, controller = Users, action = AddUser }rute . Nilai area rute dihasilkan oleh nilai default untuk area. Rute yang dibuat oleh MapAreaControllerRoute setara dengan yang berikut ini:

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

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

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

Menggunakan contoh sebelumnya, nilai { area = Blog, controller = Users, action = AddUser } rute cocok dengan tindakan berikut:

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}

Atribut [Area] adalah yang menunjukkan pengontrol sebagai bagian dari area. Pengontrol ini ada di area tersebut Blog . Pengontrol tanpa [Area] atribut bukan anggota area apa pun, dan tidak cocok ketika area nilai rute disediakan oleh perutean. Dalam contoh berikut, hanya pengontrol pertama yang tercantum yang dapat mencocokkan nilai { area = Blog, controller = Users, action = AddUser }rute .

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace2
{
    // Matches { area = Zebra, controller = Users, action = AddUser }
    [Area("Zebra")]
    public class UsersController : Controller
    {
        // GET /zebra/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace3
{
    // Matches { area = string.Empty, controller = Users, action = AddUser }
    // Matches { area = null, controller = Users, action = AddUser }
    // Matches { controller = Users, action = AddUser }
    public class UsersController : Controller
    {
        // GET /users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }
    }
}

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

Dua pengontrol pertama adalah anggota area, dan hanya cocok ketika nama area masing-masing disediakan oleh area nilai rute. Pengontrol ketiga bukan anggota area apa pun, dan hanya dapat cocok ketika tidak ada nilai yang area disediakan oleh perutean.

Dalam hal tidak ada nilai yang cocok, tidak adanya area nilai sama seperti jika nilai untuk area null atau string kosong.

Saat menjalankan tindakan di dalam area, nilai rute untuk area tersedia sebagai nilai sekitar untuk perutean yang akan digunakan untuk pembuatan URL. Ini berarti bahwa secara default area bertindak lengket untuk pembuatan URL seperti yang ditunjukkan oleh sampel berikut.

app.MapAreaControllerRoute(name: "duck_route",
                                     areaName: "Duck",
                                     pattern: "Manage/{controller}/{action}/{id?}");
app.MapControllerRoute(name: "default",
                             pattern: "Manage/{controller=Home}/{action=Index}/{id?}");
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace4
{
    [Area("Duck")]
    public class UsersController : Controller
    {
        // GET /Manage/users/GenerateURLInArea
        public IActionResult GenerateURLInArea()
        {
            // Uses the 'ambient' value of area.
            var url = Url.Action("Index", "Home");
            // Returns /Manage/Home/Index
            return Content(url);
        }

        // GET /Manage/users/GenerateURLOutsideOfArea
        public IActionResult GenerateURLOutsideOfArea()
        {
            // Uses the empty value for area.
            var url = Url.Action("Index", "Home", new { area = "" });
            // Returns /Manage
            return Content(url);
        }
    }
}

Kode berikut menghasilkan URL ke /Zebra/Users/AddUser:

public class HomeController : Controller
{
    public IActionResult About()
    {
        var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
        return Content($"URL: {url}");
    }

Definisi tindakan

Metode publik pada pengontrol, kecuali yang memiliki atribut NonAction , adalah tindakan.

Kode Sampel

Men-debug diagnostik

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

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

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

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

Tindakan dirutekan secara konvensional atau dirutekan atribut. Menempatkan rute pada pengontrol atau tindakan membuatnya dirutekan atribut. Lihat Perutean campuran untuk informasi selengkapnya.

Dokumen ini:

  • Menjelaskan interaksi antara MVC dan perutean:
    • Bagaimana aplikasi MVC khas memanfaatkan fitur perutean.
    • Mencakup keduanya:
      • Perutean konvensional biasanya digunakan dengan pengontrol dan tampilan.
      • Perutean atribut yang digunakan dengan REST API. Jika Anda terutama tertarik pada perutean untuk REST API, lompat ke bagian Perutean atribut untuk REST API .
    • Lihat Perutean untuk detail perutean tingkat lanjut.
  • Mengacu pada sistem perutean default yang ditambahkan di ASP.NET Core 3.0, yang disebut perutean titik akhir. Dimungkinkan untuk menggunakan pengontrol dengan versi perutean sebelumnya untuk tujuan kompatibilitas. Lihat panduan migrasi 2.2-3.0 untuk petunjuknya. Lihat versi 2.2 dokumen ini untuk bahan referensi pada sistem perutean warisan.

Menyiapkan rute konvensional

Startup.Configure biasanya memiliki kode yang mirip dengan yang berikut ini saat menggunakan perutean konvensional:

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

Di dalam panggilan ke UseEndpoints, MapControllerRoute digunakan untuk membuat satu rute. Rute tunggal diberi nama default rute. Sebagian besar aplikasi dengan pengontrol dan tampilan menggunakan templat rute yang mirip default dengan rute. REST API harus menggunakan perutean atribut.

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

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

  • Mengekstrak nilai { controller = Products, action = Details, id = 5 } rute dengan membuat token jalur. Ekstraksi nilai rute menghasilkan kecocokan jika aplikasi memiliki pengontrol 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 selengkapnya.

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

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

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

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

  • Cocok dengan jalur /URL .

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

Nilai untuk controller dan action memanfaatkan nilai default. id tidak menghasilkan nilai karena tidak ada segmen yang sesuai di jalur URL. / hanya cocok jika ada HomeController tindakan dan Index :

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

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

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

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

Metode MapDefaultControllerRoutekenyamanan :

endpoints.MapDefaultControllerRoute();

Menggantikan:

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

Penting

Perutean dikonfigurasi menggunakan UseRoutingmiddleware , MapControllerRoute, dan MapAreaControllerRoute . Untuk menggunakan pengontrol:

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. in ?{id?} membuatnya opsional. id digunakan untuk memetakan ke entitas model.

Dengan menggunakan rute ini default , jalur URL:

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

Pemetaan ini:

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

Menggunakan perutean konvensional dengan rute default memungkinkan pembuatan aplikasi tanpa harus membuat pola URL baru untuk setiap tindakan. Untuk aplikasi dengan tindakan gaya CRUD , memiliki konsistensi untuk URL di seluruh pengontrol:

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

Peringatan

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

  • id diatur ke 0 berdasarkan pengikatan model.
  • Tidak ada entitas yang ditemukan dalam database yang cocok id == 0.

Perutean atribut memberikan kontrol menenangkan untuk membuat ID diperlukan untuk beberapa tindakan dan bukan untuk yang lain. Menurut konvensi, dokumentasi menyertakan parameter opsional seperti id saat kemungkinan akan muncul dalam penggunaan yang benar.

Sebagian besar aplikasi harus memilih skema perutean dasar dan deskriptif sehingga URL dapat dibaca dan bermakna. Rute {controller=Home}/{action=Index}/{id?}konvensional default :

  • Mendukung skema perutean dasar dan deskriptif.
  • Adalah titik awal yang berguna untuk aplikasi berbasis UI.
  • Adalah satu-satunya templat rute yang diperlukan untuk banyak aplikasi UI web. Untuk aplikasi UI web yang lebih besar, rute lain yang menggunakan Area sering kali merupakan semua yang diperlukan.

MapControllerRoute dan MapAreaRoute :

  • Secara otomatis menetapkan nilai pesanan ke titik akhir mereka berdasarkan urutan yang dipanggil.

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

  • Tidak memiliki konsep rute.
  • Tidak memberikan jaminan pemesanan untuk eksekusi ekstensibilitas, semua titik akhir diproses sekaligus.

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

Perutean atribut dijelaskan nanti dalam dokumen ini.

Beberapa rute konvensional

Beberapa rute konvensional dapat ditambahkan ke dalam UseEndpoints dengan menambahkan lebih banyak panggilan ke MapControllerRoute dan MapAreaControllerRoute. Melakukannya memungkinkan menentukan beberapa konvensi, atau menambahkan rute konvensional yang didedikasikan untuk tindakan tertentu, seperti:

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

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

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

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

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

Contoh sebelumnya:

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

Peringatan

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

  • Tentukan konsep yang disebut rute. UseRouting menambahkan pencocokan rute ke alur middleware. 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 Perutean untuk bahan referensi tentang perutean.

Urutan perutean konvensional

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

Peringatan

Parameter catch-all mungkin salah mencocokkan rute karena bug dalam perutean. Aplikasi yang terpengaruh oleh bug ini memiliki karakteristik berikut:

  • Rute catch-all, misalnya, {**slug}"
  • Rute catch-all gagal mencocokkan permintaan yang harus cocok.
  • Menghapus rute lain membuat rute catch-all mulai berfungsi.

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

Perbaikan keikutsertaan untuk bug ini terkandung dalam .NET Core 3.1.301 SDK dan yang lebih baru. Kode berikut menetapkan sakelar internal yang memperbaiki bug ini:

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

Mengatasi tindakan ambigu

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

  • Pilih kandidat terbaik.
  • Tampilkan pengecualian.

Contohnya:

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

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

Pengontrol sebelumnya mendefinisikan dua tindakan yang cocok:

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

Ini adalah pola umum untuk pengontrol MVC:

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

Untuk mengatasi rute yang benar:

  • Edit(int, Product) dipilih ketika permintaan adalah 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. Edit(int, Product) membuat HttpPostAttribute kecocokan yang lebih baik daripada Edit(int).

Penting untuk memahami peran atribut seperti HttpPostAttribute. Atribut serupa didefinisikan untuk kata kerja HTTP lainnya. Dalam perutean konvensional, umum bagi tindakan untuk menggunakan nama tindakan yang sama saat mereka menjadi bagian dari formulir acara, kirim alur kerja formulir. Misalnya, lihat Memeriksa dua metode tindakan Edit.

Jika perutean tidak dapat memilih kandidat terbaik, sebuah AmbiguousMatchException dilemparkan, mencantumkan beberapa titik akhir yang cocok.

Nama rute konvensional

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

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

Nama rute memberi rute nama logis. Rute bernama dapat digunakan untuk pembuatan URL. Menggunakan rute bernama menyederhanakan pembuatan URL saat pengurutan rute dapat membuat pembuatan URL menjadi rumit. Nama rute harus lebar aplikasi yang unik.

Nama rute:

  • Tidak berdampak pada pencocokan URL atau penanganan permintaan.
  • Hanya digunakan untuk pembuatan URL.

Konsep nama rute diwakili dalam perutean sebagai IEndpointNameMetadata. Nama rute istilah dan nama titik akhir:

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

Perutean atribut untuk REST API

REST API harus menggunakan perutean atribut untuk memodelkan fungsionalitas aplikasi sebagai sekumpulan sumber daya di mana operasi diwakili oleh kata kerja HTTP.

Perutean atribut menggunakan sekumpulan atribut untuk memetakan tindakan langsung ke templat rute. Kode berikut StartUp.Configure khas untuk REST API dan digunakan dalam sampel berikutnya:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

Dalam kode sebelumnya, MapControllers dipanggil ke dalam UseEndpoints untuk memetakan pengontrol rute atribut.

Dalam contoh berikut:

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

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

Tindakan HomeController.Index dijalankan untuk salah satu jalur /URL , , /Home, /Home/Indexatau /Home/Index/3.

Contoh ini menyoroti perbedaan pemrograman utama antara perutean atribut dan perutean konvensional. Perutean atribut memerlukan lebih banyak input untuk menentukan rute. Rute default konvensional menangani rute dengan lebih tepat. Namun, perutean atribut memungkinkan dan memerlukan kontrol yang tepat tentang templat rute mana yang berlaku untuk setiap tindakan.

Dengan perutean atribut, pengontrol dan nama tindakan tidak memainkan bagian di mana tindakan dicocokkan, kecuali penggantian token digunakan. Contoh berikut cocok dengan URL yang sama dengan contoh sebelumnya:

public class MyDemoController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult MyIndex(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

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

Kode berikut menggunakan penggantian token untuk action dan controller:

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

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

Kode berikut berlaku [Route("[controller]/[action]")] untuk pengontrol:

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

    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Dalam kode sebelumnya, Index templat metode harus didahului / atau ~/ ke templat rute. Templat rute diterapkan ke tindakan yang dimulai dengan / atau ~/ tidak digabungkan dengan templat rute yang diterapkan ke pengontrol.

Lihat Prioritas templat rute untuk informasi tentang pemilihan templat rute.

Nama perutean yang dicadangkan

Kata kunci berikut adalah nama parameter rute yang dipesan saat menggunakan Pengontrol atau Razor Halaman:

  • action
  • area
  • controller
  • handler
  • page

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

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

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

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

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

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

Templat kata kerja HTTP

ASP.NET Core memiliki templat kata kerja HTTP berikut:

Templat rute

ASP.NET Core memiliki templat rute berikut:

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}" templat. Bagian :int templat membatasi id nilai rute ke string yang dapat dikonversi menjadi bilangan bulat. Permintaan GET ke /api/test2/int/abc:
    • Tidak cocok dengan tindakan ini.
    • Mengembalikan kesalahan 404 Tidak Ditemukan .
      [HttpGet("int/{id:int}")] // GET /api/test2/int/3
      public IActionResult GetIntProduct(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      
  • Tindakan GetInt2Product berisi {id} dalam templat, tetapi tidak 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 Permintaan Buruk 400 karena pengikatan model gagal dikonversi abc ke bilangan bulat.
      [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
      public IActionResult GetInt2Product(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      

Perutean atribut dapat menggunakan HttpMethodAttribute atribut seperti HttpPostAttribute, , HttpPutAttributedan HttpDeleteAttribute. Semua atribut kata kerja HTTP menerima templat rute. Contoh berikut menunjukkan dua tindakan yang cocok dengan templat rute yang sama:

[ApiController]
public class MyProductsController : ControllerBase
{
    [HttpGet("/products3")]
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpPost("/products3")]
    public IActionResult CreateProduct(MyProduct myProduct)
    {
        return ControllerContext.MyDisplayRouteInfo(myProduct.Name);
    }
}

Menggunakan jalur /products3URL :

  • Tindakan MyProductsController.ListProducts berjalan ketika kata kerja HTTP adalah GET.
  • Tindakan MyProductsController.CreateProduct berjalan ketika kata kerja 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 tepat tentang apa yang didukung API Anda. REST Klien API diharapkan mengetahui jalur dan kata kerja HTTP apa yang dipetakan ke operasi logis tertentu.

REST API harus menggunakan perutean atribut untuk memodelkan fungsionalitas aplikasi sebagai sekumpulan sumber daya di mana operasi diwakili oleh kata kerja HTTP. Ini berarti bahwa banyak operasi, misalnya, GET dan POST pada sumber daya logis yang sama menggunakan URL yang sama. Perutean atribut menyediakan tingkat kontrol yang diperlukan untuk merancang tata letak titik akhir publik API dengan hati-hati.

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

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

Tindakan Products2ApiController.GetProduct(int) :

  • Dijalankan dengan jalur URL seperti /products2/3
  • Tidak dijalankan dengan jalur /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 Konsumsi.

Lihat Perutean untuk deskripsi lengkap templat rute dan opsi terkait.

Untuk informasi selengkapnya tentang [ApiController], lihat atribut ApiController.

Nama rute

Kode berikut mendefinisikan nama rute :Products_List

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

Nama rute dapat digunakan untuk menghasilkan URL berdasarkan rute tertentu. Nama rute:

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

Nama rute harus unik di seluruh aplikasi.

Berbeda dengan kode sebelumnya dengan rute default konvensional, yang mendefinisikan id parameter sebagai opsional ({id?}). Kemampuan untuk menentukan API dengan tepat memiliki keuntungan, seperti mengizinkan /products dan /products/5 dikirim ke tindakan yang berbeda.

Menggabungkan rute atribut

Untuk membuat perutean atribut kurang berulang, atribut rute pada pengontrol dikombinasikan dengan atribut rute pada tindakan individual. Templat rute apa pun yang ditentukan pada pengontrol telah ditambahkan ke templat rute pada tindakan. Menempatkan atribut rute pada pengontrol membuat semua tindakan di pengontrol menggunakan perutean atribut.

[ApiController]
[Route("products")]
public class ProductsApiController : ControllerBase
{
    [HttpGet]
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

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

Dalam contoh sebelumnya:

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

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

Templat rute diterapkan ke tindakan yang dimulai dengan / atau ~/ tidak digabungkan dengan templat rute yang diterapkan ke pengontrol. Contoh berikut cocok dengan sekumpulan jalur URL yang mirip dengan rute default.

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

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

Tabel berikut menjelaskan [Route] atribut dalam kode sebelumnya:

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

Urutan rute atribut

Perutean membangun pohon dan mencocokkan semua titik akhir secara bersamaan:

  • Entri rute berulah seolah-olah ditempatkan dalam urutan yang ideal.
  • Rute yang paling spesifik memiliki kesempatan untuk dijalankan sebelum rute yang lebih umum.

Misalnya, rute atribut seperti blog/search/{topic} lebih spesifik daripada rute atribut seperti blog/{*article}. Rute blog/search/{topic} memiliki prioritas yang lebih tinggi, secara default, karena lebih spesifik. Dengan menggunakan perutean konvensional, pengembang bertanggung jawab untuk menempatkan rute dalam urutan yang diinginkan.

Rute atribut dapat mengonfigurasi pesanan Order menggunakan properti . Semua atribut rute yang disediakan kerangka kerja meliputi Order . Rute diproses sesuai dengan jenis properti yang Order naik. Urutan defaultnya adalah 0. Mengatur rute menggunakan Order = -1 eksekusi sebelum rute yang tidak mengatur pesanan. Mengatur rute menggunakan Order = 1 eksekusi setelah pemesanan rute default.

Hindari tergantung pada Order. Jika ruang URL aplikasi memerlukan nilai pesanan eksplisit untuk merutekan dengan benar, kemungkinan membingungkan klien juga. Secara umum, perutean atribut memilih rute yang benar dengan pencocokan URL. Jika urutan default yang digunakan untuk pembuatan URL tidak berfungsi, menggunakan nama rute sebagai penimpaan biasanya lebih sederhana daripada menerapkan Order properti.

Pertimbangkan dua pengontrol berikut yang keduanya menentukan pencocokan /homerute :

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

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult About(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}
public class MyDemoController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult MyIndex(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

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

/home Meminta dengan kode sebelumnya melemparkan pengecualian yang mirip dengan yang berikut ini:

AmbiguousMatchException: The request matched multiple endpoints. Matches:

 WebMvcRouting.Controllers.HomeController.Index
 WebMvcRouting.Controllers.MyDemoController.MyIndex

Order Menambahkan ke salah satu atribut rute menyelesaikan ambiguitas:

[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
    return ControllerContext.MyDisplayRouteInfo();
}

Dengan kode sebelumnya, /home menjalankan HomeController.Index titik akhir. Untuk sampai ke MyDemoController.MyIndexpermintaan , /home/MyIndex. Catatan:

  • Kode sebelumnya adalah contoh atau desain perutean yang buruk. Ini digunakan untuk mengilustrasikan Order properti .
  • Properti Order hanya menyelesaikan ambiguitas, templat tersebut tidak dapat dicocokkan. Akan lebih baik untuk menghapus [Route("Home")] templat.

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

Dalam beberapa kasus, kesalahan HTTP 500 dikembalikan dengan rute ambigu. Gunakan pengelogan untuk melihat titik akhir mana yang menyebabkan AmbiguousMatchException.

Penggantian token dalam templat rute [pengontrol], [tindakan], [area]

Untuk kenyamanan, rute atribut mendukung penggantian token dengan menyertakan token dalam kurung siku ([, ]). Token [action], [area], dan [controller] diganti dengan nilai nama tindakan, nama area, dan nama pengontrol dari tindakan tempat rute ditentukan:

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


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

Dalam kode sebelumnya:

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

Penggantian token terjadi sebagai langkah terakhir membangun rute atribut. Contoh sebelumnya berpura-pura sama dengan kode berikut:

public class Products20Controller : Controller
{
    [HttpGet("[controller]/[action]")]  // Matches '/Products20/List'
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("[controller]/[action]/{id}")]   // Matches '/Products20/Edit/{id}'
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Jika Anda membaca ini dalam bahasa selain bahasa Inggris, beri tahu kami dalam masalah diskusi GitHub ini jika Anda ingin melihat komentar kode dalam bahasa asli Anda.

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

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

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

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

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

Menggunakan transformator parameter untuk menyesuaikan penggantian token

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

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

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

RouteTokenTransformerConvention adalah konvensi model aplikasi yang:

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

Metode sebelumnya ListAll cocok /subscription-management/list-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 di Slug untuk definisi Slug.

Peringatan

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

Beberapa rute atribut

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

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

Menempatkan beberapa atribut rute pada pengontrol berarti masing-masing menggabungkan dengan masing-masing atribut rute pada metode tindakan:

[Route("Store")]
[Route("[controller]")]
public class Products6Controller : Controller
{
    [HttpPost("Buy")]       // Matches 'Products6/Buy' and 'Store/Buy'
    [HttpPost("Checkout")]  // Matches 'Products6/Checkout' and 'Store/Checkout'
    public IActionResult Buy()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Semua batasan rute kata kerja HTTP menerapkan IActionConstraint.

Saat beberapa atribut rute yang diterapkan IActionConstraint ditempatkan pada tindakan:

  • Setiap batasan tindakan dikombinasikan dengan templat rute yang diterapkan ke pengontrol.
[Route("api/[controller]")]
public class Products7Controller : ControllerBase
{
    [HttpPut("Buy")]        // Matches PUT 'api/Products7/Buy'
    [HttpPost("Checkout")]  // Matches POST 'api/Products7/Checkout'
    public IActionResult Buy()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Menggunakan beberapa rute pada tindakan mungkin tampak berguna dan kuat, lebih baik menjaga ruang URL aplikasi Anda tetap dasar dan terdefinisi dengan baik. Gunakan beberapa rute pada tindakan hanya jika diperlukan, misalnya, untuk mendukung klien yang ada.

Menentukan parameter opsional rute atribut, nilai default, dan batasan

Rute atribut mendukung sintaksis sebaris yang sama dengan rute konvensional untuk menentukan parameter opsional, nilai default, dan batasan.

public class Products14Controller : Controller
{
    [HttpPost("product14/{id:int}")]
    public IActionResult ShowProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Dalam kode sebelumnya, [HttpPost("product14/{id:int}")] menerapkan batasan rute. Tindakan Products14Controller.ShowProduct hanya dicocokkan dengan jalur URL seperti /product14/3. Bagian {id:int} templat rute membatasi segmen tersebut hanya untuk bilangan bulat.

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

Atribut rute kustom menggunakan IRouteTemplateProvider

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

  • Mencari atribut pada kelas pengontrol dan metode tindakan saat aplikasi dimulai.
  • Menggunakan atribut yang diterapkan IRouteTemplateProvider untuk membangun serangkaian rute awal.

Terapkan IRouteTemplateProvider untuk menentukan atribut rute kustom. Masing-masing IRouteTemplateProvider memungkinkan Anda menentukan satu rute dengan templat, pesanan, dan nama rute kustom:

public class MyApiControllerAttribute : Attribute, IRouteTemplateProvider
{
    public string Template => "api/[controller]";
    public int? Order => 2;
    public string Name { get; set; }
}

[MyApiController]
[ApiController]
public class MyTestApiController : ControllerBase
{
    // GET /api/MyTestApi
    [HttpGet]
    public IActionResult Get()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Metode sebelumnya Get mengembalikan Order = 2, Template = api/MyTestApi.

Menggunakan model aplikasi untuk menyesuaikan rute atribut

Model aplikasi:

  • Adalah model objek yang dibuat saat startup.
  • Berisi semua metadata yang digunakan oleh ASP.NET Core untuk merutekan dan menjalankan tindakan dalam aplikasi.

Model aplikasi mencakup semua data yang dikumpulkan dari atribut rute. Data dari atribut rute disediakan oleh IRouteTemplateProvider implementasi. Konvensi:

  • Dapat ditulis untuk mengubah model aplikasi untuk menyesuaikan perilaku perutean.
  • Dibaca saat pengaktifan aplikasi.

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

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

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

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

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

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

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

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

Misalnya, pengontrol berikut tidak menggunakan NamespaceRoutingConvention:

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

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

Metode NamespaceRoutingConvention.Apply:

  • Tidak melakukan apa pun jika pengontrol dirutekan atributnya.
  • Mengatur templat pengontrol berdasarkan namespace, dengan 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:

  • Basisnya namespace adalah My.Application.
  • Nama lengkap pengontrol sebelumnya adalah My.Application.Admin.Controllers.UsersController.
  • Mengatur NamespaceRoutingConvention templat pengontrol 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.

Tindakan dirutekan secara konvensional atau atribut yang dirutekan. Menempatkan rute pada pengontrol atau tindakan membuatnya atribut dirutekan. Tindakan yang menentukan rute atribut tidak dapat dicapai melalui rute konvensional dan sebaliknya. Atribut rute apa pun pada pengontrol membuat semua tindakan dalam atribut pengontrol dirutekan.

Perutean atribut dan perutean konvensional menggunakan mesin perutean yang sama.

Pembuatan URL dan nilai sekitar

Aplikasi dapat menggunakan fitur pembuatan URL perutean untuk menghasilkan tautan URL ke tindakan. Menghasilkan URL menghilangkan URL hardcoding, membuat kode lebih kuat dan dapat dipertahankan. Bagian ini berfokus pada fitur pembuatan URL yang disediakan oleh MVC dan hanya mencakup dasar-dasar cara kerja pembuatan URL. Lihat Perutean untuk deskripsi terperinci tentang pembuatan URL.

Antarmuka IUrlHelper adalah elemen infrastruktur yang mendasar antara MVC dan perutean untuk pembuatan URL. Instans IUrlHelper tersedia melalui Url properti di pengontrol, tampilan, dan komponen tampilan.

Dalam contoh berikut, IUrlHelper antarmuka digunakan melalui Controller.Url properti untuk menghasilkan URL ke tindakan lain.

public class UrlGenerationController : Controller
{
    public IActionResult Source()
    {
        // Generates /UrlGeneration/Destination
        var url = Url.Action("Destination");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    public IActionResult Destination()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Jika aplikasi menggunakan rute konvensional default, nilai url variabel adalah string /UrlGeneration/Destinationjalur URL . Jalur URL ini dibuat dengan perutean dengan menggabungkan:

  • Nilai rute dari permintaan saat ini, yang disebut nilai sekitar.
  • Nilai yang diteruskan ke Url.Action dan mengganti nilai tersebut ke dalam templat rute:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}

result: /UrlGeneration/Destination

Setiap parameter rute dalam templat rute memiliki nilainya digantikan dengan mencocokkan nama dengan nilai dan nilai sekitar. Parameter rute yang tidak memiliki nilai dapat:

  • Gunakan nilai default jika memilikinya.
  • Dilewati jika bersifat opsional. Misalnya, id dari templat {controller}/{action}/{id?}rute .

Pembuatan URL gagal jika parameter rute yang diperlukan tidak memiliki nilai yang sesuai. Jika pembuatan URL gagal untuk rute, rute berikutnya dicoba hingga semua rute telah dicoba atau kecocokan ditemukan.

Contoh Url.Action sebelumnya mengasumsikan perutean konvensional. Pembuatan URL berfungsi sama dengan perutean atribut, meskipun konsepnya berbeda. Dengan perutean konvensional:

  • Nilai rute digunakan untuk memperluas templat.
  • Nilai rute untuk controller dan action biasanya muncul dalam templat tersebut. Ini berfungsi karena URL yang cocok dengan perutean mematuhi konvensi.

Contoh berikut menggunakan perutean atribut:

public class UrlGenerationAttrController : Controller
{
    [HttpGet("custom")]
    public IActionResult Source()
    {
        var url = Url.Action("Destination");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    [HttpGet("custom/url/to/destination")]
    public IActionResult Destination()
    {
       return ControllerContext.MyDisplayRouteInfo();
    }
}

Tindakan Source dalam kode sebelumnya menghasilkan custom/url/to/destination.

LinkGenerator ditambahkan di ASP.NET Core 3.0 sebagai alternatif untuk IUrlHelper. LinkGenerator menawarkan fungsionalitas yang serupa tetapi lebih fleksibel. Setiap metode pada IUrlHelper memiliki keluarga metode yang LinkGenerator sesuai juga.

Membuat URL menurut nama tindakan

Url.Action, LinkGenerator.GetPathByAction, dan semua kelebihan beban terkait semuanya dirancang untuk menghasilkan titik akhir target dengan menentukan nama pengontrol dan nama tindakan.

Saat menggunakan Url.Action, nilai rute saat ini untuk controller dan action disediakan oleh runtime:

  • Nilai controller dan action merupakan bagian dari nilai dan nilai sekitar. 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 pembuatan URL dan gagal.
  • Nilai yang diinginkan dan cd harus ditentukan untuk menghasilkan URL.

Anda mungkin mengharapkan untuk mengalami masalah ini dengan rute {controller}/{action}/{id?}default . Masalah ini jarang terjadi dalam praktiknya karena Url.Action selalu secara eksplisit menentukan nilai controller dan action .

Beberapa kelebihan beban Url.Action mengambil objek nilai rute untuk menyediakan nilai untuk parameter rute selain controller dan action. Objek nilai rute sering digunakan dengan id. Contohnya,Url.Action("Buy", "Products", new { id = 17 }). Objek nilai rute:

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

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

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

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

Kode berikut menghasilkan URL absolut:

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

Untuk membuat URL absolut, gunakan salah satu hal berikut:

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

Hasilkan URL menurut rute

Kode sebelumnya menunjukkan pembuatan URL dengan meneruskan pengontrol dan nama tindakan. IUrlHelper juga menyediakan keluarga metode Url.RouteUrl . Metode ini mirip dengan Url.Action, tetapi tidak menyalin nilai action saat ini dan controller ke nilai rute. Penggunaan yang paling umum dari Url.RouteUrl:

  • Menentukan nama rute untuk menghasilkan URL.
  • Umumnya tidak menentukan pengontrol atau nama tindakan.
public class UrlGeneration2Controller : Controller
{
    [HttpGet("")]
    public IActionResult Source()
    {
        var url = Url.RouteUrl("Destination_Route");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    [HttpGet("custom/url/to/destination2", Name = "Destination_Route")]
    public IActionResult Destination()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

File berikut Razor menghasilkan tautan HTML ke Destination_Route:

<h1>Test Links</h1>

<ul>
    <li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>

Hasilkan URL dalam HTML dan Razor

IHtmlHelperHtmlHelper menyediakan metode Html.BeginForm dan Html.ActionLink untuk menghasilkan <form> elemen dan <a> masing-masing. Metode ini menggunakan metode Url.Action untuk menghasilkan URL dan mereka menerima argumen serupa. Pendamping Url.RouteUrl untuk HtmlHelper adalah Html.BeginRouteForm dan Html.RouteLink yang memiliki fungsionalitas serupa.

TagHelpers menghasilkan URL melalui form TagHelper dan <a> TagHelper. Kedua penggunaan IUrlHelper ini untuk implementasinya. Lihat Tag Helper dalam formulir untuk informasi selengkapnya.

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

Pembuatan URL dalam Hasil Tindakan

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

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

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

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

Kasus khusus untuk rute konvensional khusus

Perutean konvensional dapat menggunakan jenis definisi rute khusus yang disebut rute konvensional khusus. Dalam contoh berikut, rute bernama blog adalah rute konvensional khusus:

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

Menggunakan definisi rute sebelumnya, Url.Action("Index", "Home") menghasilkan jalur / URL menggunakan default rute, tetapi mengapa? Anda mungkin menebak nilai { controller = Home, action = Index } rute akan cukup untuk menghasilkan URL menggunakan blog, dan hasilnya adalah /blog?action=Index&controller=Home.

Rute konvensional khusus mengandalkan perilaku khusus nilai default yang tidak memiliki parameter rute yang sesuai yang mencegah rute terlalu serakah dengan pembuatan URL. Dalam hal ini nilai defaultnya adalah { controller = Blog, action = Article }, dan tidak controller juga action muncul sebagai parameter rute. Saat perutean melakukan pembuatan URL, nilai yang disediakan harus cocok dengan nilai default. Pembuatan URL menggunakan blog gagal karena nilainya { controller = Home, action = Index } tidak cocok { controller = Blog, action = Article }. Perutean kemudian kembali untuk mencoba default, yang berhasil.

Daerah

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

  • Perutean namespace layanan untuk tindakan pengontrol.
  • Struktur folder untuk tampilan.

Menggunakan area memungkinkan aplikasi memiliki beberapa pengontrol dengan nama yang sama, selama memiliki area yang berbeda. Menggunakan area membuat hierarki untuk tujuan perutean dengan menambahkan parameter rute lain, area ke controller dan action. Bagian ini membahas bagaimana perutean berinteraksi dengan area. Lihat Area untuk detail tentang bagaimana area digunakan dengan tampilan.

Contoh berikut mengonfigurasi MVC untuk menggunakan rute konvensional default dan area rute untuk yang area bernama Blog:

app.UseEndpoints(endpoints =>
{
    endpoints.MapAreaControllerRoute("blog_route", "Blog",
        "Manage/{controller}/{action}/{id?}");
    endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});

Dalam kode sebelumnya, MapAreaControllerRoute dipanggil untuk membuat "blog_route". Parameter kedua, "Blog", adalah nama area.

Saat mencocokkan jalur URL seperti /Manage/Users/AddUser, "blog_route" rute menghasilkan nilai { area = Blog, controller = Users, action = AddUser }rute . Nilai area rute dihasilkan oleh nilai default untuk area. Rute yang dibuat oleh MapAreaControllerRoute setara dengan yang berikut ini:

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

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

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

Menggunakan contoh sebelumnya, nilai { area = Blog, controller = Users, action = AddUser } rute cocok dengan tindakan berikut:

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}

Atribut [Area] adalah yang menunjukkan pengontrol sebagai bagian dari area. Pengontrol ini ada di area tersebut Blog . Pengontrol tanpa [Area] atribut bukan anggota area apa pun, dan tidak cocok ketika area nilai rute disediakan oleh perutean. Dalam contoh berikut, hanya pengontrol pertama yang tercantum yang dapat mencocokkan nilai { area = Blog, controller = Users, action = AddUser }rute .

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace2
{
    // Matches { area = Zebra, controller = Users, action = AddUser }
    [Area("Zebra")]
    public class UsersController : Controller
    {
        // GET /zebra/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace3
{
    // Matches { area = string.Empty, controller = Users, action = AddUser }
    // Matches { area = null, controller = Users, action = AddUser }
    // Matches { controller = Users, action = AddUser }
    public class UsersController : Controller
    {
        // GET /users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }
    }
}

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

Dua pengontrol pertama adalah anggota area, dan hanya cocok ketika nama area masing-masing disediakan oleh area nilai rute. Pengontrol ketiga bukan anggota area apa pun, dan hanya dapat cocok ketika tidak ada nilai yang area disediakan oleh perutean.

Dalam hal tidak ada nilai yang cocok, tidak adanya area nilai sama seperti jika nilai untuk area null atau string kosong.

Saat menjalankan tindakan di dalam area, nilai rute untuk area tersedia sebagai nilai sekitar untuk perutean yang akan digunakan untuk pembuatan URL. Ini berarti bahwa secara default area bertindak lengket untuk pembuatan URL seperti yang ditunjukkan oleh sampel berikut.

app.UseEndpoints(endpoints =>
{
    endpoints.MapAreaControllerRoute(name: "duck_route", 
                                     areaName: "Duck",
                                     pattern: "Manage/{controller}/{action}/{id?}");
    endpoints.MapControllerRoute(name: "default",
                                 pattern: "Manage/{controller=Home}/{action=Index}/{id?}");
});
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace4
{
    [Area("Duck")]
    public class UsersController : Controller
    {
        // GET /Manage/users/GenerateURLInArea
        public IActionResult GenerateURLInArea()
        {
            // Uses the 'ambient' value of area.
            var url = Url.Action("Index", "Home");
            // Returns /Manage/Home/Index
            return Content(url);
        }

        // GET /Manage/users/GenerateURLOutsideOfArea
        public IActionResult GenerateURLOutsideOfArea()
        {
            // Uses the empty value for area.
            var url = Url.Action("Index", "Home", new { area = "" });
            // Returns /Manage
            return Content(url);
        }
    }
}

Kode berikut menghasilkan URL ke /Zebra/Users/AddUser:

public class HomeController : Controller
{
    public IActionResult About()
    {
        var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
        return Content($"URL: {url}");
    }

Definisi tindakan

Metode publik pada pengontrol, kecuali yang memiliki atribut NonAction , adalah tindakan.

Kode Sampel

Men-debug diagnostik

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

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