Основой для определения маршрута служит его шаблон. На основе шаблона маршрута система маршрутизации определяет, будет ли обрабатываться запрос, а если и будет, то каким контроллером и каким методом контроллера.
Например, возьмем простейший проект ASP.NET Core и в файле Program.cs один маршрут:
var builder = WebApplication.CreateBuilder(args); // добавляем поддержку контроллеров с представлениями builder.Services.AddControllersWithViews(); var app = builder.Build(); // устанавливаем сопоставление маршрутов с контроллерами app.MapControllerRoute( name: "default", pattern: "{controller}/{action}"); app.Run();
Здесь шаблон маршрута выглядит следующим образом:
"{controller}/{action}"
Шаблон маршрута может иметь параметры. Параметры имеют имя и определяются в шаблоне маршрута внутри фигурных скобок: {название_параметра}
. Например,
в шаблоне маршрута выше определено два параметра: controller
и action
.
И когда приходит запрос к приложению, система маршрутизации сопоставляет запрошенный путь, если сопоставление прошло успешно, то система маршрутизации использует параметр "controller" для получении имени контроллера, а параметр "action" - для определения действия этого контроллера, который будет обрабатывать запрос.
Если в шаблоне маршрута идут подряд несколько параметров, то между ними должен располагаться разделитель. Обычно в качестве разделителя выступает слеш (как в данном случае), который оформляет отдельный сегмент в пути запроса. То есть в случае выше каждый отдельный параметр маршрута отделен от другого слешем, поэтому будет представлять отдельный сегмент пути запроса. Например, если пришел запрос по пути "/home/index/", то первый сегмент "/home/" будет проецироваться на параметр "controller", поэтому будет выбран контроллер Home. Второй сегмент - "/index/" будет проецироваться на параметр "action", соответственно для обработки этого запроса будет выбран метод Index контроллера Home.
Пусть в проекте определен контроллер HomeController со следующим кодом:
using Microsoft.AspNetCore.Mvc; namespace MvcApp.Controllers { public class HomeController : Controller { public string Index() => "Index Page"; public string About() => "About Page"; } }
Таким образом, при запросе Home/Index, запрос будет обрабатываться методом Index контроллера Home, а запрос Home/About - методом About того же контроллера.
Шаблон маршрута может содержать дополнительные параметры, которые можно получить через параметры метода. Например, определим следующие маршруты:
var builder = WebApplication.CreateBuilder(args); // добавляем поддержку контроллеров с представлениями builder.Services.AddControllersWithViews(); var app = builder.Build(); // устанавливаем сопоставление маршрутов с контроллерами app.MapControllerRoute(name: "default", pattern: "{controller}/{action}/{id}"); app.MapControllerRoute(name: "name_age", pattern: "{controller}/{action}/{name}/{age}"); app.Run();
Здесь первый маршрут - "default" принимает третий параметр - id, который располагается в третьем сегменте строки запроса.
Второй маршрут - "name_age" принимает дополнительно два параметра - name и age, который располагаются соответственно в третьем сегменте и четвертом сегментах строки запроса.
Для тестирования маршрутов определим следующий контроллер:
using Microsoft.AspNetCore.Mvc; namespace MvcApp.Controllers { public class HomeController : Controller { public string Index(int id) => $"Index Page. Id: {id}"; public string About(string name, int age) => $"About Page. Name: {name} Age: {age}"; } }
И в данном случае, если к приложению придет запрос типа "Home/Index/6", система маршрутизации сможет сопоставить этот запрос с первым маршрутом - маршрутом "default", который также содержит из трех сегментов, и значение последнего сегмента - число 6 будет передано в метод Index через параметр id:
Если же к приложению придет запрос из четырех сегментов типа "Home/About/Tom/37", то система маршрутизации сможет сопоставить этот запрос со вторым маршрутом - маршрутом "name_age". Тогда значение третьего сегмента будет передано в метод через параметр name, а значение четвертого сегмента - через параметр age:
Шаблоны маршрутов могут иметь статические сегменты, которые не связывются с параметрами маршрутов. Например, определим следующий маршрут:
// устанавливаем сопоставление маршрутов с контроллерами app.MapControllerRoute( name: "default", pattern: "api/{controller}/{action}/{id}");
В данном случае шаблон маршрута начинается со статического сегмента api/
. Таким образом, этому маршруту будут соответствовать все маршруты, которые состоят из четырех сегментов,
где первый сегмент равен "/api", например, запрос https://localhost:7288/api/Home/Index/6
Параметры маршрута могут быть необязательными. Чтобы определить параметр как необязательный, после его названия указывается знак вопроса. Например, определим следующее приложение:
app.MapControllerRoute( name: "default", pattern: "{controller}/{action}/{id?}");
В этом шаблоне маршрута третий сегмент представляет параметр id, который помечен как необязательный. А это значит, что мы можем в запросе игнорировать значение для этого сегмента. Например, данный шаблон будет соответствовать двух- и трехсегментным запросам, например, двум следующим url:
/home/index /home/index/23
Для тестирования определим следующий контроллер:
using Microsoft.AspNetCore.Mvc; namespace MvcApp.Controllers { public class HomeController : Controller { public string Index(int? id) { if(id is not null) return $"Product Id: {id}"; return "Product List"; } } }
Здесь, если параметр id определен, то метод Index возвращает одну строку, если неопределен - то другую:
Необязательные параметры следует помещать в конце шаблона маршрута, как в случае с параметром id в примере выше. То есть, к примеру, шаблон "{controller}/{action}/{id?}/{name}" не будет работать корректно, если мы для параметра id не передадим значение. А вот шаблон "{controller}/{action}/{name}/{id?}" будет работать нормально.
Параметрам маршрута также можно назначить значения по умолчанию на случай, если им не переданы значения:
var builder = WebApplication.CreateBuilder(args); // добавляем поддержку контроллеров с представлениями builder.Services.AddControllersWithViews(); var app = builder.Build(); // устанавливаем сопоставление маршрутов с контроллерами app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
Здесь определен шаблон маршрута, который состоит из трех параметров. Параметр "controller" имеет значение по умолчанию "Home". Параметр "action" имеет значение по умолчанию "Index". Параметр "id" определен как необязательный. В итоге при различных запросах у нас получатся следующие значения:
Запрос | Параметры запроса |
https://localhost:7256/ | controller=Home action=Index |
https://localhost:7256/Book | controller=Book action=Index |
https://localhost:7256/Book/Show | controller=Book action=Show |
https://localhost:7256/Book/Show/2 | controller=Book action=Show id=2 |
Для установки значений по умолчанию также можно применять параметр defaults метода MapControllerRoute(). Этот параметр представляет объект, свойства которого соответствуют параметрам маршрута. Например, определим следующий маршрут:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews(); var app = builder.Build(); app.MapControllerRoute( name: "default", pattern: "{action}", defaults: new { controller = "Home", action = "Index"}); app.Run();
Здесь шаблон маршрута состоит из одного сегмента, который соответствует параметру "action", то есть представляет действие контроллера. А параметр defaults:
defaults: new { controller = "Home", action = "Index"}
Устанавливает, что в качестве контроллера по умолчанию будет использоваться контроллер Home, а в качестве действия - метод Index.
Например, пусть у нас будет следующий HomeController:
public class HomeController : Controller { public string Index() => "Index Page"; public string About() => "About Page"; }
В этом случае запрос типа https://localhost:7288/ будет обрабатываться методом Index контроллера Home, а запрос https://localhost:7288/About - методом About.
При использовании значений по умолчанию мы можем вовсе не использовать в шаблоне параметры маршрута. Например:
app.MapControllerRoute( name: "info", pattern: "contact/info", defaults: new { controller = "Home", action = "About"});
В данном случае запрос https://localhost:7288/contact/info будет обрабатываться методом About контроллера Home.
Для параметров маршрута в MVC, также как и в общем в ASP.NET Core, можно устанавливать ограничения. Подробно ограничения были расписаны в статье Ограничения маршрутов в ASP.NET. В MVC применяется те же ограничения, что и в общем в ASP.NET Core, поэтому в данной статье я не буду их подробно описывать.
Ограничения можно установить непосредственно в шаблоне маршрута:
app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id:int?}");
В данном случае указывается, что параметр id может иметь либо значение типа int, либо значение null
Второй способ установки ограничений представляет параметр constraints метода MapControllerRoute:
using Microsoft.AspNetCore.Routing.Constraints; // для типа IntRouteConstraint var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews(); var app = builder.Build(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}", constraints: new {id= new IntRouteConstraint()}); // ограничения маршрутов app.Run();
Параметр constraints принимает объект, в котором свойства соответствуют по названиям параметрам маршрутов, а значения свойств - ограничения,
применяемые к одноименным параметрам маршрутов. Так, в данном случае к параметру id
применяется ограничение IntRouteConstraint
, которое указывает, что
id должно представлять значение типа int.