Pull to refresh

ASP.NET MVC Урок 4. Routing

Reading time 5 min
Views 144K
Цель урока: Изучить инициализацию маршрутизации. Деление на Areas в приложении. Принципы создания маршрутизации.

Controller и Action.

Веб-сайт состоит из страниц. Вообще, веб-сайт состоит не из страниц, а из ответов на запросы, но какую-то определенную структуру мы хотим иметь.

Собственно, у нас есть маршрутизатор, который должен определить, какой метод у какого контроллера вызвать. Поэтому, два основных параметра, которые обязательно должны быть это controller и action. Рассмотрим как задается шаблон маршрутов в App_Start/RouteConfig.cs:
routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );

Таким образом, url = «/Role/Create/2» будет означать, что мы находим контроллер RoleController, в этом контроллере находим Create метод, который может принимать (а может и не принимать) параметр id. И если он принимает параметр id, то id = 2 или даже id = “2”, в зависимости, что за тип будет.

Defaults обозначает, что если строка будет “/Role/Create” – то в случае, что Create метод с параметром id и по умолчанию не стоит значение, или не может быть создано default(), то по возможности будет выбран другой метод (мы же полиморфны). Иначе будет сгенерирована ошибка: не найден метод, готовый принять такой запрос.

 public ActionResult Index(int? id)
        {
            //ok
            return View();
        }
…
        public ActionResult Index(int id = 0)
        {
            //ok
            return View();
        }
…
        public ActionResult Index(int id)
        {
            //fail
            return View();
        }


В случае url = “/Role” будет вызван метод Index в контроллере RoleController.

В случае url = “/” будет вызван метод Index в контроллере HomeController.

Рассмотрим на примерах.

BaseController

Создадим несколько контроллеров, но для того, чтобы не создавать постоянно доступ к репозиторию, первоначально создадим базовый контроллер BaseController:
public abstract class BaseController : Controller
    {
        [Inject]
        public IRepository Repository { get; set; }
    }

И
  public class HomeController : BaseController
    {
        public ActionResult Index()
        {
            return View();
        }
    }


View Home/Index.cshtml:
@{
    ViewBag.Title = "LessonProject";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>LessonProject</h2>

<p>
    <div class="menu">
    <a href="@Url.Action("Index", "Role")">Роли</a>
    @Html.ActionLink("Пользователи",  "Index", "User")
    </div>
    
</p>


Добавим для просмотра RoleController:
    public class RoleController : BaseController
    {
        public ActionResult Index()
        {
            var roles = Repository.Roles.ToList();
            return View(roles);
        }
    }

И
@model IList<LessonProject.Model.Role>

@{
    ViewBag.Title = "Roles";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Roles</h2>

<p>
    @foreach (var role in Model)
    {
        <div class="item">
            <span class="id">
                @role.ID
            </span>
            <span class="name">
                @role.Name
            </span>
            <span class="Code">
                @role.Code
            </span>
        </div>
    }
</p>



И такой же UserController, собственно, сделаем.

Рассмотрим, как задаются маршруты с помощью Url.Action() и Html.ActionLink().

Url.Action() – принимает параметры, первым – action, потом – controller, потом через new {} – можно задавать и перечислять все остальные.
Html.ActionLink() – формирует тег , первый параметр – наименование ссылки, второй – action, третий – controller, четвертым(или пятым) параметром идут другие атрибуты тега, если мы хотим добавить другие параметры для маршрутизации – мы должны явно указать пятым параметром null. Т.е. :
@Html.ActionLink(“Пользователь под номером 1”, “Item”, “User”, new {id = 1}, null)

Если не указать null:
@Html.ActionLink(“Пользователь под номером 1”, “Item”, “User”, new {id = 1})
то ссылка будет выглядеть так:
Пользователь под номером 1

Порядок объявления маршрутов

Создадим маршрут, который будет расположен ранее и относится только к RoleController:
routes.MapRoute(
                name: "Role",
                url: "roli/{action}/{id}",
                defaults: new { controller = "Role", action = "Index", id = UrlParameter.Optional }
            );

Строка “roli/{action}/{id}” однозначно задает имя контроллера в секции defaults. А action является параметром.
Результат. Ссылка стала:
Роли
Уберем из defaults action=”Index”:
Роли
Поместим после объявления “Defaults”:
Роли

Такая ссылка получилась, потому что вышестоящим правилом маршрута “default” можно задать путь к Role/Index:
  context.MapRoute(
                name : "default",
                url : "{controller}/{action}/{id}",
                defaults : new { controller = "Home", action = "Index", id = UrlParameter.Optional },
            );

Еще надо рассмотреть один метод, который называется IgnoreRoute, он указывает маршрутизатору, что если url подходит под шаблон, то нужно вернуть ресурс, который расположен по тому адресу, а не пытаться находить контроллер.

Ограничения (Constrains)

Мы можем добавить в маршрутизацию ограничения запросов браузера, которые соответствуют особому маршруту. Например, id должен быть в нашем случае числовым:
routes.MapRoute(
                name: "Role",
                url: "roli/{action}/{id}",
                defaults: new { controller = "Role", action = "Index", id = UrlParameter.Optional },
                constraints : new {id = @"\d+"}
            );

При передаче id, которое не соответствует данному условию, будет или выбран другой маршрут, или метод не будет найден и ссылка будет битой:
Для:
<a href="@Url.Action("Index", "Role", new { id = "privet" })">Роли

Будет:
Роли
А для:
<a href="@Url.Action("Index", "Role", new { id = "1" })">Роли
Будет:
Роли
Примечание: Более подробно об ограничениях можно узнать тут: http://stephenwalther.com/archive/2008/08/07/asp-net-mvc-tip-30-create-custom-route-constraints.aspx

Areas

Чтобы разделить различные по свойствам функциональные модули веб-приложения. Например, форум отдельно от всего сайта. Мы же поделим на часть Admin – где будет админка, и всё остальное, которое будет называться Default.



Сделаем следующие действия:
  • Переименуем _Default в Default везде.
  • Перенесем свои контроллеры (кроме BaseController) в папку Areas/Default/Controllers
  • Переименуем namespace для контроллеров в LessonProject.Areas.Default.Controllers
  • Исправляем DefaultAreaRegistration.

    Здесь важно обратить внимание на новый параметр для задания маршрутов: namespaces, он указывает, из каких namespace можно выбирать контроллеры для разбора маршрута:
      public class DefaultAreaRegistration : AreaRegistration
        {
            public override string AreaName
            {
                get
                {
                    return "Default";
                }
            }
    
            public override void RegisterArea(AreaRegistrationContext context)
            {
                context.MapRoute(
                    name : "default",
                    url : "{controller}/{action}/{id}",
                    defaults : new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                    namespaces : new [] { "LessonProject.Areas.Default.Controllers" }
                );
            }
        }
    

  • В Global.asax есть строка AreaRegistration.RegisterAllAreas(); которая регистрирует все найденные объявления area, но она нам не подходит, так как если DefaultArea зарегистрировать раньше AdminArea, то будет срабатывать маршрутизация Default, а в админку мы уже не сможем попасть, поэтому исправляем:
      var adminArea = new AdminAreaRegistration();
                var adminAreaContext = new AreaRegistrationContext(adminArea.AreaName, RouteTable.Routes);
                adminArea.RegisterArea(adminAreaContext);
    
                var defaultArea = new DefaultAreaRegistration();
                var defaultAreaContext = new AreaRegistrationContext(defaultArea.AreaName, RouteTable.Routes);
                defaultArea.RegisterArea(defaultAreaContext);
    

  • Регистрацию маршрутов убираем (не api).

Запускаем.

Все исходники находятся по адресу https://bitbucket.org/chernikov/lessons
Tags:
Hubs:
+43
Comments 3
Comments Comments 3

Articles