Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Perutean adalah cara Api Web mencocokkan URI dengan tindakan. Web API 2 mendukung jenis perutean baru, yang disebut perutean atribut. Seperti namanya, perutean atribut menggunakan atribut untuk menentukan rute. Perutean atribut memberi Anda lebih banyak kontrol atas URI di API web Anda. Misalnya, Anda dapat dengan mudah membuat URI yang menjelaskan hierarki sumber daya.
Gaya perutean sebelumnya, yang disebut perutean berbasis konvensi, masih didukung penuh. Bahkan, Anda dapat menggabungkan kedua teknik dalam proyek yang sama.
Topik ini menunjukkan cara mengaktifkan perutean atribut dan menjelaskan berbagai opsi untuk perutean atribut. Untuk tutorial end-to-end yang menggunakan perutean atribut, lihat Membuat REST API dengan Perutean Atribut di Web API 2.
Prasyarat
Visual Studio 2017 Edisi Komunitas, Profesional, atau Perusahaan
Atau, gunakan NuGet Package Manager untuk menginstal paket yang diperlukan. Dari menu Alat di Visual Studio, pilih NuGet Package Manager, lalu pilih Package Manager Console. Masukkan perintah berikut di jendela Package Manager Console:
Install-Package Microsoft.AspNet.WebApi.WebHost
Mengapa Perutean Atribut?
Rilis pertama Web API menggunakan perutean berbasis konvensi . Dalam jenis perutean tersebut, Anda menentukan satu atau beberapa templat rute, yang pada dasarnya merupakan string berparameter. Saat kerangka kerja menerima permintaan, kerangka kerja cocok dengan URI dengan templat rute. Untuk informasi selengkapnya tentang perutean berbasis konvensi, lihat Perutean di ASP.NET Web API.
Salah satu keuntungan dari perutean berbasis konvensi adalah bahwa templat didefinisikan di satu tempat, dan aturan perutean diterapkan secara konsisten di semua pengontrol. Sayangnya, perutean berbasis konvensi membuatnya sulit untuk mendukung pola URI tertentu yang umum dalam RESTful API. Misalnya, sumber daya sering berisi sumber daya anak: Pelanggan memiliki pesanan, film memiliki aktor, buku memiliki penulis, dan sebagainya. Adalah wajar untuk membuat URI yang mencerminkan hubungan ini:
/customers/1/orders
Jenis URI ini sulit dibuat menggunakan perutean berbasis konvensi. Meskipun dapat dilakukan, hasilnya tidak menskalakan dengan baik jika Anda memiliki banyak pengontrol atau jenis sumber daya.
Dengan perutean atribut, mudah untuk menentukan rute untuk URI ini. Anda cukup menambahkan atribut ke tindakan pengontrol:
[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... }
Berikut adalah beberapa pola lain yang memudahkan perutean atribut.
Penerapan versi API
Dalam contoh ini, "/api/v1/products" akan dirutekan ke pengontrol yang berbeda dari "/api/v2/products".
/api/v1/products
/api/v2/products
Segmen URI yang kelebihan beban
Dalam contoh ini, "1" adalah nomor pesanan, tetapi peta "tertunda" ke koleksi.
/orders/1
/orders/pending
Beberapa jenis parameter
Dalam contoh ini, "1" adalah nomor pesanan, tetapi "2013/06/16" menentukan tanggal.
/orders/1
/orders/2013/06/16
Mengaktifkan Perutean Atribut
Untuk mengaktifkan perutean atribut, panggil MapHttpAttributeRoutes selama konfigurasi. Metode ekstensi ini didefinisikan dalam kelas System.Web.Http.HttpConfigurationExtensions .
using System.Web.Http;
namespace WebApplication
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
// Other Web API configuration not shown.
}
}
}
Perutean atribut dapat dikombinasikan dengan perutean berbasis konvensi . Untuk menentukan rute berbasis konvensi, panggil metode MapHttpRoute .
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Attribute routing.
config.MapHttpAttributeRoutes();
// Convention-based routing.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Untuk informasi selengkapnya tentang mengonfigurasi API Web, lihat Mengonfigurasi ASP.NET Web API 2.
Catatan: Migrasi dari WEB API 1
Sebelum Web API 2, templat proyek Web API menghasilkan kode seperti ini:
protected void Application_Start()
{
// WARNING - Not compatible with attribute routing.
WebApiConfig.Register(GlobalConfiguration.Configuration);
}
Jika perutean atribut diaktifkan, kode ini akan memberikan pengecualian. Jika Anda meningkatkan proyek Web API yang ada untuk menggunakan perutean atribut, pastikan untuk memperbarui kode konfigurasi ini ke yang berikut:
protected void Application_Start()
{
// Pass a delegate to the Configure method.
GlobalConfiguration.Configure(WebApiConfig.Register);
}
Catatan
Untuk informasi selengkapnya, lihat Mengonfigurasi API Web dengan ASP.NET Hosting.
Menambahkan Atribut Rute
Berikut adalah contoh rute yang ditentukan menggunakan atribut :
public class OrdersController : ApiController
{
[Route("customers/{customerId}/orders")]
[HttpGet]
public IEnumerable<Order> FindOrdersByCustomer(int customerId) { ... }
}
String "customers/{customerId}/orders" adalah templat URI untuk rute tersebut. API Web mencoba mencocokkan URI permintaan dengan templat. Dalam contoh ini, "pelanggan" dan "pesanan" adalah segmen literal, dan "{customerId}" adalah parameter variabel. URI berikut akan cocok dengan templat ini:
http://localhost/customers/1/ordershttp://localhost/customers/bob/ordershttp://localhost/customers/1234-5678/orders
Anda dapat membatasi pencocokan dengan menggunakan batasan, yang dijelaskan nanti dalam topik ini.
Perhatikan bahwa parameter "{customerId}" dalam templat rute cocok dengan nama parameter customerId dalam metode . Saat Api Web memanggil tindakan pengontrol, API mencoba mengikat parameter rute. Misalnya, jika URI adalah http://example.com/customers/1/orders, Api Web mencoba mengikat nilai "1" ke parameter customerId dalam tindakan.
Templat URI dapat memiliki beberapa parameter:
[Route("customers/{customerId}/orders/{orderId}")]
public Order GetOrderByCustomer(int customerId, int orderId) { ... }
Metode pengontrol apa pun yang tidak memiliki atribut rute menggunakan perutean berbasis konvensi. Dengan begitu, Anda dapat menggabungkan kedua jenis perutean dalam proyek yang sama.
Metode HTTP
WEB API juga memilih tindakan berdasarkan metode HTTP permintaan (GET, POST, dll). Secara default, Web API mencari kecocokan yang tidak peka huruf besar/kecil dengan awal nama metode pengontrol. Misalnya, metode pengontrol bernama PutCustomers cocok dengan permintaan HTTP PUT.
Anda dapat mengambil alih konvensi ini dengan mendekorasi metode dengan salah satu atribut berikut:
- [HttpDelete]
- [HttpGet]
- [HttpHead]
- [HttpOptions]
- [HttpPatch]
- [HttpPost]
- [HttpPut]
Dalam contoh berikut, Api Web memetakan metode CreateBook ke permintaan HTTP POST.
[Route("api/books")]
[HttpPost]
public HttpResponseMessage CreateBook(Book book) { ... }
Untuk semua metode HTTP lainnya, termasuk metode non-standar, gunakan atribut AcceptVerbs , yang mengambil daftar metode HTTP.
// WebDAV method
[Route("api/books")]
[AcceptVerbs("MKCOL")]
public void MakeCollection() { }
Awalan Rute
Seringkali, rute dalam pengontrol semuanya dimulai dengan awalan yang sama. Contohnya:
public class BooksController : ApiController
{
[Route("api/books")]
public IEnumerable<Book> GetBooks() { ... }
[Route("api/books/{id:int}")]
public Book GetBook(int id) { ... }
[Route("api/books")]
[HttpPost]
public HttpResponseMessage CreateBook(Book book) { ... }
}
Anda dapat mengatur awalan umum untuk seluruh pengontrol dengan menggunakan atribut [RoutePrefix] :
[RoutePrefix("api/books")]
public class BooksController : ApiController
{
// GET api/books
[Route("")]
public IEnumerable<Book> Get() { ... }
// GET api/books/5
[Route("{id:int}")]
public Book Get(int id) { ... }
// POST api/books
[Route("")]
public HttpResponseMessage Post(Book book) { ... }
}
Gunakan tilde (~) pada atribut metode untuk mengambil alih awalan rute:
[RoutePrefix("api/books")]
public class BooksController : ApiController
{
// GET /api/authors/1/books
[Route("~/api/authors/{authorId:int}/books")]
public IEnumerable<Book> GetByAuthor(int authorId) { ... }
// ...
}
Awalan rute dapat mencakup parameter:
[RoutePrefix("customers/{customerId}")]
public class OrdersController : ApiController
{
// GET customers/1/orders
[Route("orders")]
public IEnumerable<Order> Get(int customerId) { ... }
}
Batasan Rute
Batasan rute memungkinkan Anda membatasi bagaimana parameter dalam templat rute dicocokkan. Sintaks umumnya adalah "{parameter:constraint}". Contohnya:
[Route("users/{id:int}")]
public User GetUserById(int id) { ... }
[Route("users/{name}")]
public User GetUserByName(string name) { ... }
Di sini, rute pertama hanya akan dipilih jika segmen "id" URI adalah bilangan bulat. Jika tidak, rute kedua akan dipilih.
Tabel berikut mencantumkan batasan yang didukung.
| Batasan | Deskripsi | Contoh |
|---|---|---|
| alpha | Cocok dengan karakter alfabet Latin huruf besar atau huruf kecil (a-z, A-Z) | {x:alpha} |
| bool | Cocok dengan nilai Boolean. | {x:bool} |
| tanggalwaktu | Cocok dengan nilai DateTime . | {x:datetime} |
| decimal | Cocok dengan nilai desimal. | {x:desimal} |
| double | Cocok dengan nilai floating-point 64-bit. | {x:double} |
| float | Cocok dengan nilai floating-point 32-bit. | {x:float} |
| guid | Cocok dengan nilai GUID. | {x:guid} |
| int | Cocok dengan nilai bilangan bulat 32-bit. | {x:int} |
| length | Mencocokkan string dengan panjang yang ditentukan atau dalam rentang panjang yang ditentukan. | {x:length(6)} {x:length(1,20)} |
| long | Cocok dengan nilai bilangan bulat 64-bit. | {x:long} |
| maks | Mencocokkan bilangan bulat dengan nilai maksimum. | {x:max(10)} |
| maxlength | Mencocokkan string dengan panjang maksimum. | {x:maxlength(10)} |
| mnt | Cocok dengan bilangan bulat dengan nilai minimum. | {x:min(10)} |
| minlength | Cocok dengan string dengan panjang minimum. | {x:minlength(10)} |
| rentang | Cocok dengan bilangan bulat dalam rentang nilai. | {x:range(10,50)} |
| regex | Cocok dengan ekspresi reguler. | {x:regex(^\d{3}-\d{3}-\d{4}$)} |
Perhatikan bahwa beberapa batasan, seperti "min", mengambil argumen dalam tanda kurung. Anda dapat menerapkan beberapa batasan ke parameter, dipisahkan oleh titik dua.
[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { ... }
Batasan Rute Kustom
Anda bisa membuat batasan rute kustom dengan menerapkan antarmuka IHttpRouteConstraint . Misalnya, batasan berikut membatasi parameter ke nilai bilangan bulat bukan nol.
public class NonZeroConstraint : IHttpRouteConstraint
{
public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName,
IDictionary<string, object> values, HttpRouteDirection routeDirection)
{
object value;
if (values.TryGetValue(parameterName, out value) && value != null)
{
long longValue;
if (value is long)
{
longValue = (long)value;
return longValue != 0;
}
string valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
if (Int64.TryParse(valueString, NumberStyles.Integer,
CultureInfo.InvariantCulture, out longValue))
{
return longValue != 0;
}
}
return false;
}
}
Kode berikut menunjukkan cara mendaftarkan batasan:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var constraintResolver = new DefaultInlineConstraintResolver();
constraintResolver.ConstraintMap.Add("nonzero", typeof(NonZeroConstraint));
config.MapHttpAttributeRoutes(constraintResolver);
}
}
Sekarang Anda dapat menerapkan batasan dalam rute Anda:
[Route("{id:nonzero}")]
public HttpResponseMessage GetNonZero(int id) { ... }
Anda juga dapat mengganti seluruh kelas DefaultInlineConstraintResolver dengan menerapkan antarmuka IInlineConstraintResolver . Melakukannya akan menggantikan semua batasan bawaan, kecuali implementasi IInlineConstraintResolver Anda secara khusus menambahkannya.
Parameter URI Opsional dan Nilai Default
Anda dapat membuat parameter URI opsional dengan menambahkan tanda tanya ke parameter rute. Jika parameter rute bersifat opsional, Anda harus menentukan nilai default untuk parameter metode.
public class BooksController : ApiController
{
[Route("api/books/locale/{lcid:int?}")]
public IEnumerable<Book> GetBooksByLocale(int lcid = 1033) { ... }
}
Dalam contoh ini, /api/books/locale/1033 dan /api/books/locale mengembalikan sumber daya yang sama.
Atau, Anda dapat menentukan nilai default di dalam templat rute, sebagai berikut:
public class BooksController : ApiController
{
[Route("api/books/locale/{lcid:int=1033}")]
public IEnumerable<Book> GetBooksByLocale(int lcid) { ... }
}
Ini hampir sama dengan contoh sebelumnya, tetapi ada sedikit perbedaan perilaku ketika nilai default diterapkan.
- Dalam contoh pertama ("{lcid:int?}"), nilai default 1033 ditetapkan langsung ke parameter metode, sehingga parameter akan memiliki nilai yang tepat ini.
- Dalam contoh kedua ("{lcid:int=1033}"), nilai default "1033" melewati proses pengikatan model. Model-binder default akan mengonversi "1033" ke nilai numerik 1033. Namun, Anda dapat menyambungkan pengikat model kustom, yang mungkin melakukan sesuatu yang berbeda.
(Dalam kebanyakan kasus, kecuali Anda memiliki pengikat model kustom di alur Anda, dua formulir akan setara.)
Nama Rute
Di Api Web, setiap rute memiliki nama. Nama rute berguna untuk menghasilkan tautan, sehingga Anda dapat menyertakan tautan dalam respons HTTP.
Untuk menentukan nama rute, atur properti Nama pada atribut . Contoh berikut menunjukkan cara mengatur nama rute, dan juga cara menggunakan nama rute saat membuat tautan.
public class BooksController : ApiController
{
[Route("api/books/{id}", Name="GetBookById")]
public BookDto GetBook(int id)
{
// Implementation not shown...
}
[Route("api/books")]
public HttpResponseMessage Post(Book book)
{
// Validate and add book to database (not shown)
var response = Request.CreateResponse(HttpStatusCode.Created);
// Generate a link to the new book and set the Location header in the response.
string uri = Url.Link("GetBookById", new { id = book.BookId });
response.Headers.Location = new Uri(uri);
return response;
}
}
Urutan Rute
Ketika kerangka kerja mencoba mencocokkan URI dengan rute, kerangka kerja mengevaluasi rute dalam urutan tertentu. Untuk menentukan pesanan, atur properti Pesanan pada atribut rute. Nilai yang lebih rendah dievaluasi terlebih dahulu. Nilai pesanan default adalah nol.
Berikut adalah bagaimana total pemesanan ditentukan:
Bandingkan properti Pesanan atribut rute.
Lihat setiap segmen URI di templat rute. Untuk setiap segmen, pesan sebagai berikut:
- Segmen harfiah.
- Parameter rute dengan batasan.
- Parameter rute tanpa batasan.
- Segmen parameter kartubebas dengan batasan.
- Segmen parameter kartubebas tanpa batasan.
Dalam kasus dasi, rute diurutkan oleh perbandingan string ordinal yang tidak peka huruf besar/kecil (OrdinalIgnoreCase) dari templat rute.
Berikut adalah contohnya. Misalkan Anda menentukan pengontrol berikut:
[RoutePrefix("orders")]
public class OrdersController : ApiController
{
[Route("{id:int}")] // constrained parameter
public HttpResponseMessage Get(int id) { ... }
[Route("details")] // literal
public HttpResponseMessage GetDetails() { ... }
[Route("pending", RouteOrder = 1)]
public HttpResponseMessage GetPending() { ... }
[Route("{customerName}")] // unconstrained parameter
public HttpResponseMessage GetByCustomer(string customerName) { ... }
[Route("{*date:datetime}")] // wildcard
public HttpResponseMessage Get(DateTime date) { ... }
}
Rute ini diurutkan sebagai berikut.
- pesanan/detail
- orders/{id}
- orders/{customerName}
- orders/{*date}
- pesanan/tertunda
Perhatikan bahwa "detail" adalah segmen harfiah dan muncul sebelum "{id}", tetapi "tertunda" muncul terakhir karena properti Pesanan adalah 1. (Contoh ini mengasumsikan tidak ada pelanggan bernama "detail" atau "tertunda". Secara umum, cobalah untuk menghindari rute ambigu. Dalam contoh ini, templat rute yang lebih baik untuk GetByCustomer adalah "customers/{customerName}" )