ASP.NET Web API'sinde Yönlendirme ve Eylem Seçimi

Bu makalede, ASP.NET Web API'lerinin http isteğini denetleyicideki belirli bir eyleme nasıl yönlendirdiği açıklanmaktadır.

Not

Yönlendirmeye üst düzey bir genel bakış için bkz . ASP.NET Web API'sinde yönlendirme.

Bu makalede yönlendirme işleminin ayrıntılarına bakabilirsiniz. Bir Web API projesi oluşturur ve bazı isteklerin beklediğiniz gibi yönlendirilemediğini fark ederseniz, umarım bu makale yardımcı olur.

Yönlendirmenin üç ana aşaması vardır:

  1. URI'yi bir yol şablonuyla eşleştirme.
  2. Bir denetleyici seçme.
  3. Eylem seçme.

İşlemin bazı bölümlerini kendi özel davranışlarınızla değiştirebilirsiniz. Bu makalede, varsayılan davranışı açıkliyorum. Sonunda, davranışı özelleştirebileceğiniz yerleri not ediyorum.

Rota Şablonları

Yol şablonu URI yoluna benzer, ancak küme ayraçlarıyla gösterilen yer tutucu değerleri olabilir:

"api/{controller}/public/{category}/{id}"

Bir yol oluşturduğunuzda, yer tutucuların bazıları veya tümü için varsayılan değerler sağlayabilirsiniz:

defaults: new { category = "all" }

Ayrıca bir URI kesiminin yer tutucuyla eşleşmesini kısıtlayan kısıtlamalar da sağlayabilirsiniz:

constraints: new { id = @"\d+" }   // Only matches if "id" is one or more digits.

Çerçeve, URI yolundaki segmentleri şablonla eşleştirmeye çalışır. Şablondaki değişmez değerler tam olarak eşleşmelidir. Kısıtlamaları belirtmediğiniz sürece yer tutucu herhangi bir değerle eşleşir. Çerçeve, konak adı veya sorgu parametreleri gibi URI'nin diğer bölümleriyle eşleşmiyor. Çerçeve, yol tablosunda URI ile eşleşen ilk yolu seçer.

İki özel yer tutucu vardır: "{controller}" ve "{action}".

  • "{controller}" denetleyicinin adını sağlar.
  • "{action}" eylemin adını sağlar. Web API'sinde her zamanki kural "{action}" öğesini atlar.

Varsayılanları

Varsayılan değerleri sağlarsanız, yol bu segmentlerin eksik olduğu bir URI ile eşleşecektir. Örnek:

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{category}",
    defaults: new { category = "all" }
);

URI'ler http://localhost/api/products/all ve http://localhost/api/products önceki yol ile eşleş. İkinci URI'de, eksik {category} segmente varsayılan değeri allatanır.

Rota Sözlüğü

Çerçeve bir URI için eşleşme bulursa, her yer tutucunun değerini içeren bir sözlük oluşturur. Anahtarlar, küme ayraçları dahil olmak üzere yer tutucu adlarıdır. Değerler URI yolundan veya varsayılan değerlerden alınır. Sözlük IHttpRouteData nesnesinde depolanır.

Bu yol eşleştirme aşamasında, özel "{controller}" ve "{action}" yer tutucuları diğer yer tutucular gibi işlenir. Bunlar yalnızca sözlükte diğer değerlerle birlikte depolanır.

Varsayılan değer RouteParameter.Optional özel değerine sahip olabilir. Bir yer tutucuya bu değer atanırsa, değer yol sözlüğüne eklenmez. Örnek:

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{category}/{id}",
    defaults: new { category = "all", id = RouteParameter.Optional }
);

"api/products" URI yolu için yol sözlüğü şunları içerir:

  • denetleyici: "products"
  • category: "all"

Ancak "api/products/toys/123" için rota sözlüğü şunları içerecektir:

  • denetleyici: "products"
  • kategori: "toys"
  • id: "123"

Varsayılanlar, rota şablonunun herhangi bir yerinde görünmeyen bir değer de içerebilir. Yol eşleşirse, bu değer sözlükte depolanır. Örnek:

routes.MapHttpRoute(
    name: "Root",
    routeTemplate: "api/root/{id}",
    defaults: new { controller = "customers", id = RouteParameter.Optional }
);

URI yolu "api/root/8" ise sözlük iki değer içerir:

  • denetleyici: "müşteriler"
  • id: "8"

Denetleyici Seçme

Denetleyici seçimi IHttpControllerSelector.SelectController yöntemi tarafından işlenir. Bu yöntem bir HttpRequestMessage örneği alır ve bir HttpControllerDescriptor döndürür. Varsayılan uygulama DefaultHttpControllerSelector sınıfı tarafından sağlanır. Bu sınıf basit bir algoritma kullanır:

  1. "denetleyici" anahtarı için yol sözlüğüne bakın.
  2. Bu anahtarın değerini alın ve denetleyici türü adını almak için "Denetleyici" dizesini ekleyin.
  3. Bu tür ada sahip bir Web API denetleyicisi arayın.

Örneğin, yol sözlüğü "controller" = "products" anahtar-değer çiftini içeriyorsa, denetleyici türü "ProductsController" olur. Eşleşen bir tür veya birden çok eşleşme yoksa, çerçeve istemciye bir hata döndürür.

3. adım için DefaultHttpControllerSelector , Web API denetleyicisi türlerinin listesini almak için IHttpControllerTypeResolver arabirimini kullanır. Varsayılan IHttpControllerTypeResolver uygulaması, (a) IHttpController uygulayan, (b) soyut olmayan ve (c) "Denetleyici" ile biten bir ada sahip olan tüm genel sınıfları döndürür.

Eylem Seçimi

Denetleyiciyi seçtikten sonra çerçeve, IHttpActionSelector.SelectAction yöntemini çağırarak eylemi seçer. Bu yöntem bir HttpControllerContext alır ve bir HttpActionDescriptor döndürür.

Varsayılan uygulama ApiControllerActionSelector sınıfı tarafından sağlanır. Bir eylemi seçmek için aşağıdakilere bakar:

  • İsteğin HTTP yöntemi.
  • Varsa, yol şablonundaki "{action}" yer tutucusu.
  • Denetleyicideki eylemlerin parametreleri.

Seçim algoritmasına bakmadan önce denetleyici eylemleriyle ilgili bazı şeyleri anlamamız gerekir.

Denetleyicideki hangi yöntemler "eylemler" olarak kabul edilir? Bir eylemi seçerken çerçeve yalnızca denetleyicideki genel örnek yöntemlerine bakar. Ayrıca, "özel ad" yöntemlerini (oluşturucular, olaylar, işleç aşırı yüklemeleri vb.) ve ApiController sınıfından devralınan yöntemleri dışlar.

HTTP Yöntemleri. Çerçeve yalnızca isteğin HTTP yöntemiyle eşleşen ve aşağıdaki gibi belirlenen eylemleri seçer:

  1. HTTP yöntemini şu öznitelikle belirtebilirsiniz: AcceptVerbs, HttpDelete, HttpGet, HttpHead, HttpOptions, HttpPatch, HttpPost veya HttpPut.
  2. Aksi takdirde, denetleyici yönteminin adı "Get", "Post", "Put", "Delete", "Head", "Options" veya "Patch" ile başlıyorsa, eylem bu HTTP yöntemini destekler.
  3. Yukarıdakilerden hiçbiri değilse, yöntemi POST'yi destekler.

Parametre Bağlamaları. Parametre bağlaması, Web API'sinde bir parametre için değer oluşturma yöntemidir. Parametre bağlama için varsayılan kural aşağıdadır:

  • Basit türler URI'den alınır.
  • Karmaşık türler istek gövdesinden alınır.

Basit türler tüm .NET Framework temel türlerinin yanı sıra DateTime, Decimal, Guid, String ve TimeSpan'ı içerir. Her eylem için en fazla bir parametre istek gövdesini okuyabilir.

Not

Varsayılan bağlama kurallarını geçersiz kılmak mümkündür. Bkz. Arka planda WebAPI Parametresi bağlaması.

Bu arka planda eylem seçimi algoritması aşağıdadır.

  1. Denetleyicide HTTP isteği yöntemiyle eşleşen tüm eylemlerin listesini oluşturun.

  2. Yol sözlüğünde "eylem" girdisi varsa, adı bu değerle eşleşmeyen eylemleri kaldırın.

  3. Eylem parametrelerini URI ile eşleştirmeyi aşağıdaki gibi deneyin:

    1. Her eylem için, bağlamanın parametreyi URI'den aldığı basit bir tür olan parametrelerin listesini alın. İsteğe bağlı parametreleri hariç tutun.
    2. Bu listeden, yol sözlüğünde veya URI sorgu dizesinde her parametre adı için bir eşleşme bulmayı deneyin. Eşleşmeler büyük/küçük harfe duyarsızdır ve parametre sırasına bağlı değildir.
    3. Listedeki her parametrenin URI'de eşleşmesi olan bir eylem seçin.
    4. Bu ölçütlere uyan bir eylem varsa, en çok parametre eşleşmesi olan eylemi seçin.
  4. [NonAction] özniteliğiyle eylemleri yoksayın.

3. adım muhtemelen en kafa karıştırıcı adımdır. Temel fikir, bir parametrenin değerini URI'den, istek gövdesinden veya özel bir bağlamadan alabilmesidir. URI'den gelen parametreler için, URI'nin gerçekten bu parametre için yolda (yol sözlüğü aracılığıyla) veya sorgu dizesinde bir değer içerdiğinden emin olmak istiyoruz.

Örneğin, aşağıdaki eylemi göz önünde bulundurun:

public void Get(int id)

id parametresi URI'ye bağlanır. Bu nedenle, bu eylem yalnızca yol sözlüğünde veya sorgu dizesinde "id" değerini içeren bir URI ile eşleşebilir.

İsteğe bağlı parametreler isteğe bağlı olduğundan özel durumlardır. İsteğe bağlı bir parametre için bağlamanın URI'den değeri alamamasının bir sakıncası yoktur.

Karmaşık türler farklı bir nedenle özel durumdur. Karmaşık bir tür yalnızca özel bağlama aracılığıyla URI'ye bağlanabilir. Ancak bu durumda, çerçeve parametrenin belirli bir URI'ye bağlanıp bağlanmayacağını önceden anlayamaz. Bunu öğrenmek için bağlamayı çağırması gerekir. Seçim algoritmasının amacı, bağlamaları çağırmadan önce statik açıklamadan bir eylem seçmektir. Bu nedenle, karmaşık türler eşleşen algoritmanın dışında tutulur.

Eylem seçildikten sonra tüm parametre bağlamaları çağrılır.

Özet:

  • Eylemin isteğin HTTP yöntemiyle eşleşmesi gerekir.
  • Varsa, eylem adı yol sözlüğündeki "eylem" girişiyle eşleşmelidir.
  • Eylemin her parametresi için, parametre URI'den alınırsa, parametre adı yol sözlüğünde veya URI sorgu dizesinde bulunmalıdır. (karmaşık türlerdeki isteğe bağlı parametreler ve parametreler dışlanır.)
  • En fazla sayıda parametreyi eşleştirmeyi deneyin. En iyi eşleşme, parametresiz bir yöntem olabilir.

Genişletilmiş Örnek

Yol:

routes.MapHttpRoute(
    name: "ApiRoot",
    routeTemplate: "api/root/{id}",
    defaults: new { controller = "products", id = RouteParameter.Optional }
);
routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

Denetleyici:

public class ProductsController : ApiController
{
    public IEnumerable<Product> GetAll() {}
    public Product GetById(int id, double version = 1.0) {}
    [HttpGet]
    public void FindProductsByName(string name) {}
    public void Post(Product value) {}
    public void Put(int id, Product value) {}
}

HTTP isteği:

GET http://localhost:34701/api/products/1?version=1.5&details=1

Yol Eşleştirme

URI, "DefaultApi" adlı yolla eşleşir. Yol sözlüğü aşağıdaki girdileri içerir:

  • denetleyici: "ürünler"
  • id: "1"

Yol sözlüğü sorgu dizesi parametreleri olan "version" ve "details" değerlerini içermez, ancak bunlar eylem seçimi sırasında yine de dikkate alınır.

Denetleyici Seçimi

Yol sözlüğündeki "denetleyici" girdisinden denetleyici türü olur ProductsController.

Eylem Seçimi

HTTP isteği bir GET isteğidir. GET'i destekleyen denetleyici eylemleri , GetByIdve FindProductsByName'dırGetAll. Yol sözlüğü "eylem" için bir giriş içermediğinden, eylem adıyla eşleşmemiz gerekmez.

Ardından, yalnızca GET eylemlerine bakarak eylemlerin parametre adlarını eşleştirmeye çalışacağız.

Eylem Eşleştirecek Parametreler
GetAll yok
GetById "id"
FindProductsByName "name"

İsteğe bağlı bir parametre olduğundan sürüm parametresinin GetById dikkate alınmadığını fark edin.

GetAll yöntemi önemsiz bir şekilde eşleşir. GetById Yol sözlüğü "id" içerdiğinden yöntemi de eşleşir. FindProductsByName yöntemi eşleşmiyor.

GetById yöntemi, tek bir parametreyle eşleştiğinden ve parametresi olmadığı için GetAllkazanır. yöntemi aşağıdaki parametre değerleriyle çağrılır:

  • id = 1
  • sürüm = 1.5

Sürüm seçim algoritmasında kullanılmamış olsa da parametrenin değerinin URI sorgu dizesinden geldiğine dikkat edin.

Uzantı Noktaları

Web API'sinde yönlendirme işleminin bazı bölümleri için uzantı noktaları sağlanır.

Arabirim Description
IHttpControllerSelector Denetleyiciyi seçer.
IHttpControllerTypeResolver Denetleyici türlerinin listesini alır. DefaultHttpControllerSelector bu listeden denetleyici türünü seçer.
IAssembliesResolver Proje derlemelerinin listesini alır. IHttpControllerTypeResolver arabirimi, denetleyici türlerini bulmak için bu listeyi kullanır.
IHttpControllerActivator Yeni denetleyici örnekleri oluşturur.
IHttpactionSelector Eylemi seçer.
IHttpactionInvoker Eylemi çağırır.

Bu arabirimlerden herhangi birine kendi uygulamanızı sağlamak için HttpConfiguration nesnesinde Services koleksiyonunu kullanın:

var config = GlobalConfiguration.Configuration;
config.Services.Replace(typeof(IHttpControllerSelector), new MyControllerSelector(config));