ASP.NET MVC 5’de Attribute Routing
Selamlar;
ASP.NET MVC’de Routing aslında URI ile action’ın eşleştirilmesinden başka birşey değildir. Mvc 5 ile yeni bir routing tanımlama sistemi gelmiştir. Adı da attribute routing dir. Hem tanımlaması daha kolay, hem de daha detaylı bir tanımlama ortamı sağlamaktadır.
ASP.NET MVC önceki versiyonlarında routing RouteConfig.cs altında aşağıdaki gibi tanımlanırdı.
1 2 3 4 5 6 |
routes.MapRoute( name: "BookPage", url: "{bookId}/{bookTitle}", defaults: new { controller = "Books", action = "Read" }, constraints: new { bookId = "\\d+" } ); |
Yeni sistemde routing şu an detayına bakmadan aşağıdaki gibi kolaylıkla tanımlanabilir.
1 2 |
[Route("{bookId:int}/{bookTitle}")] public ActionResult Read(int bookId) { ... } |
Öncelikle Attribute Routing’in kullanılabilmesi için RegisterRoutes altında MapMvcAttributeRoutes()’un aşağıdaki gibi tanımlanması gerekmektedir:
1 2 3 4 5 6 |
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapMvcAttributeRoutes(); } |
Route Tanımlama: Aşağıda Index() action’ının route’u aşağıda “books” olarak, Index(string title) action’ı “books/{title}” olarak ve Detail(string title) action’ı “books/{title}/detail” olarak tanımlanmıştır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class HomeController : Controller { // GET: Home // örnek:/books [Route("books")] public ActionResult Index() { return View(); } // örnek:/books/codeReview [Route("books/{title}")] public ActionResult Index(string title) { ViewBag.Title = title; return View(); } // örnek:/books/codeReview/detail [Route("books/{title}/detail")] public ActionResult Detail(string title) { ViewBag.Title = title; return View(); } } |
Route Prefix: Kök yolun belirlendiği attribute’dur. Böylece aşağıda görüldüğü gibi Actionlar’da ana route’un, örneğin books’un belirtilmesine gerek yoktur.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[RoutePrefix("books")] public class HomeController : Controller { // örnek:/books [Route] public ActionResult Index() { return View(); } // örnek:/books/codeReview [Route("{title}")] public ActionResult Index(string title) { ViewBag.Title = title; return View(); } // örnek:/books/codeReview/detail [Route("{title}/detail")] public ActionResult Detail(string title) { ViewBag.Title = title; return View(); } } |
Ayrıca istenir ise RoutePrefix “~” işareti ile Action tarafında ezilebilir. Aşağıdaki örnekte “reviews” kök route’u Index() action’ı için ezilerek “books” yapılmıştır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[RoutePrefix("reviews")] public class HomeController : Controller { // örnek:/books [Route("~/books")] public ActionResult Index() { return View(); } // örnek: /reviews/detail/KitapDetayı [Route("detail/{title=Detay Sayfası}")] public ActionResult Detail(string title) { ViewBag.Title = title; return View(); } } |
Optional Uri ve Default Değer Atama: Aşağıda görüldüğü gibi parametre yanına “?” konarak route parametrelerinin null değer alabilmesi sağlanmaktadır.
1 2 3 4 5 6 7 8 |
// örnek: books/ // örnek: books/Kırmızı Pazartesi [Route("books/{title?}")] public ActionResult Index(string title) { ViewBag.Title = title; return View(); } |
Yine aşağıda görüldüğü gibi route parametresine herhangi bir değer gelmediğinde default belirlenen bir değer kolaylıkla atanabilmektedir.
1 2 3 4 5 6 |
[Route("books/{title=Default Başlık}")] public ActionResult Index(string title) { ViewBag.Title = title; return View(); } |
Default Route: Aşağıda görüldüğü gibi RoutePrefix ile kök route “reviews” olarak belirlenmiştir. Route attribute değeri olarak da “action=Index” yazılarak default gidilecek action “Index” olarak belirtilmiştir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[RoutePrefix("reviews")] [Route("{action=Index}")] public class HomeController : Controller { // örnek: /reviews public ActionResult Index() { return View(); } // örnek: /reviews/detail public ActionResult Detail() { string title = "Detay Sayfası"; ViewBag.Title = title; return View(); } } |
Route Constraint: Genel yazım şekli {parameter:constraint} dir. Amaç route’da alınacak parametreye bir takım kurallar tanımlamaktır. Örneğin aşağıda ilkin Index() methodu sayısal ‘id’ değer bekliyen bir action {id:int} iken ikinci tanımlanan Index() string ‘name’ bekleyen {name} bir action’dır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// örnek: /books/8 [Route("~/books/{id:int}")] public ActionResult Index(int id) { ViewBag.Title = id; return View(); } // örnek /books/NoName [Route("~/books/{name}")] public ActionResult Index(string name) { ViewBag.Title = name; return View(); } |
Örneğin aşağıda Index() action’ı için beklenen sayısal id değeri en küçük “1” olacak şekilde {id:int:min(1)} bir kural tanımlanmıştır.
1 2 3 4 5 6 7 8 9 |
// örnek:/books/5 çalışır // örnek:/books/10000000000 çalışmaz çünkü int.MaxValue'den büyük // örnek:/books/0 çalışmaz çünkü tanımlanan min(1) değerinden küçük [Route("~/books/{id:int:min(1)}")] public ActionResult Index(int id) { ViewBag.Title = id; return View(); } |
Örneğin aşağıda Index() action’ının string parametresi maxlength(5) olarak atanmıştır. Yani maximum karakter sayısı 5 dir. Aynı zamanda nullable bir parametredir. “/books/test” örneğinde “test” 5 karakterden küçük olduğu için kural sağlanmıştır. Aynı şekilde “books/deneme_yazısı” örneğinde “deneme_yazısı” 5 karakterden büyük olduğu için kural sağlanmamıştır. Son olarak “/books/” örneğinde paramtere boş değer alabildiği için kural sağlanmıştır.
1 2 3 4 5 6 7 8 9 |
// örnek: /books/test çalışır. // örnek: /books/deneme_yazısı çalışmaz // örnek: /books/ çalışır [Route("~/books/{name:maxlength(5)?}")] public ActionResult Index(string name) { ViewBag.Title = name; return View(); } |
Alttaki Route Constraint listesi msdn.com’dan alınmıştır.
Custom Route Constraint: Bazen duruma göre özel kurallar yazılması gerekebilir. İşte bu durumda Custom Route Constraintler devreye girer. Aşağıdaki örnekte kabul edilebilecek bir isim listesi alınacak. Bunun dışında gelen isimler kabul edilmeyecek. Bunun için öncelikle AllowListConstraint class’ı oluşturulur. Constructor ‘ında izin verilecek string liste “|” işareti ile ayrılarak atanır. Match() methodu ile ilgili izin verilen liste gezilerek gelen değerin bu listede olup olmadığına bakılarak bool bir değer dönülür.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class AllowListConstraint : IRouteConstraint { private readonly string[] validOptions; public AllowListConstraint(string options) { validOptions = options.Split('|'); } public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { object value; if (values.TryGetValue(parameterName, out value) && value != null) { return validOptions.Contains(value.ToString(), StringComparer.OrdinalIgnoreCase); } return false; } } |
İlgili RouteConstraint RouteConfig classında aşağıdaki gibi tanımlanır.
1 2 3 |
var constraintsResolver = new DefaultInlineConstraintResolver(); constraintsResolver.ConstraintMap.Add("allowlist", typeof(Controllers.AllowListConstraint)); routes.MapMvcAttributeRoutes(constraintsResolver); |
Aşağıdaki örnekte Index() action’ı aldığı name parametresine “bora|duru” tanımlaması ile sadece “bora ve duru” isimlerine izin verilmiştir. Yani “books/bora” veya “books/duru” haricinde bu action’a erişilemez.
1 2 3 4 5 6 |
[Route("~/books/{name:allowlist(bora|duru)}")] public ActionResult Index(string name) { ViewBag.Title = name; return View(); } |
Route Name: Aşağıdaki örnekte de görüldüğü gibi route’a isim verilerek Uri’ye view’da erişim kolaylaşmıştır.
1 2 3 4 5 6 |
[Route("detail/{title=Detay Sayfası}",Name ="Detay")] public ActionResult Detail(string title) { ViewBag.Title = title; return View(); } |
1 |
<a href="@Url.RouteUrl("Detay")">Detay</a> |
Areas: Bir controller’ı [RouteArea] attribute’ü ile bir guruba atayabiliriz. Böylece AreaRegistration classdan ilgili area kolaylıkla kaldırılabilir. Ayrıca Controllerlar bir guruba atanmış olur. Aşağıdaki örnekde Index()’e erişim “/library/reviews/books/5” şeklinde yapılmaktadır.
1 2 3 4 5 6 7 8 9 10 11 |
[RouteArea("library")] [RoutePrefix("reviews")] public class HomeController : Controller { // örnek /library/reviews/books/5 [Route("books/{id:min(2)}")] public ActionResult Index(int id) { ViewBag.Title = id; return View(); } |
Aşağıdaki link tıklandığında “library/reviews/detail” şeklinde bir url’e gitmektedir. Burada “library” yukarıda tanımladığımız area’yı temsil etmektedir.
1 |
<a href="@Url.Action("Detail", "Home" , new { area="library" })">Detay</a> |
Böylece ASP.NET MVC 5’de route işlemlerinde neler yapabileceğimizi gördük. Bize sağladığı kolaylıkları ve faydaları mümkün olduğunca incelemeye çalıştık.
Yeni bir makalede görüşmek üzere hepinize hoşçakalın.
Source: https://devblogs.microsoft.com/aspnet/attribute-routing-in-asp-net-mvc-5/
Çok güzel açıklamışsınız. Paylaşım için teşekkür ederim.
Ben teşekkür ederim Çağtay..
yeni başlayanlar için güzel anlatım :)
Teşekkürler.
Teşekkürler Bu Konuyuda Sayenizde Anlamış Bulunmaktayım .
Teşekkürler..
Teşekkürler Bora Bey.
Ben teşekkür ederim..
Merhaba, eksik bir şey var var buradakileri uygulamaya uğraştım ama route çalışmadı. Başka yerlere baktım attr route ların çalışması için registerRoutes bölümüne ” routes.MapMvcAttributeRoutes(); ” şeklinde ekleme yapmak gerek güncellerseniz sevinirim
Teşekkürler,
İlk fırsatta güncellerim. 3.5 yıl önceki bir makale :)
İyi çalışmalar.
[Route(“{NewsCategori}”)]=Kategori
[Route(“{NewsTitle}”)]=Haber Detay
Bu şekilde 2 route yaptım ama 2 sine yönlendirme yaparken çakışıyorum
örnek haberler.com yapmış kategori ye göre url haber başlığına göre detay sayfası
Selamlar ilker,
Şu makaleye bir bakarmısın?
http://www.borakasmer.com/net-core-mvcde-bir-haber-basligini-urle-koyma/
Faydalı bilgi için teşekkürler
Ben teşekkür ederim.
Hocam Selamlar,
Örneğin /Blog/Blog-Kategorisi ve /Blog/blog-yazisi-detayi gibi route tanımlayabiliyor muyuz? Ben blogun içerisinde bir kategoriye de tıklasam /Blog/blog-kategorisi sonra yazıya da tıklasam /Blog/blog-yazisi diye görünmesini istiyorum. Blog anadizinin de çalışsın 2 sayfada da böyle bir şey mümkün mü? Çünkü bir kaç sitede böyle bir şey gördüm ama MVC de nasıl yapılır çözemedim çakışma oluyor.
Selamlar Emre,
Evet mümkün. SEO çalışması için yapılacak her şey mümkündür :)
İyi çalışmalar.
Merhaba hocam;
Yazınız için teşekkürler. Benim şöyle bir sorum olacak.
Örn: CategoriesController üzerinde [Route(“kategoriler”)] yaptım daha sonra ProductController içinde [Route(“{CategoryName}”)] yaptığımda ikisi çakışma yapıyor.
Route Attribute için Name tanımlası yapıp @Url.RouteUrl ile denedim yinede olmuyor. Farklı bi çözüm varmıdır.