Осваиваем ASP.NET MVC вместе. Введение

    Немного предыстории


    Я с другом всецело интересуемся веб-разработкой и всем что с ней связано. В этом году университету дали заказ написать веб-портал для студентов, в котором можно будет получить последнюю информацию о оценках, расписание… Но об этом потом. Естесственно «Конструкторское Бюро» (так называется отдел который отвечает за разработку программного обеспечения в университете), не долго думая, решили перебросить задание на студентов. Так уж случилось, что мы попали в число этих студентов и на выбор нам предложили ASP.NET Web Forms или PHP Symfony. И дабы усложнить себе задание и выучить что-то новое, мы попросили разрешить нам сделать задание на ASP.NET MVC. Проект сейчас всецело развивается, и я думаю как закончим, мы выложим исходные коды проекта сюда. Но об этом тоже потом. И так, дабы как-нибудь систематизировать наши знания и получить советы от профессионалов, мы решили начать серию статей, которые могут помогут начать другим людям изучать этот прекрасный фреймворк. Ну что ж? Начнем!

    И так, для роботы нам понадобится как минимум следующие вещи:

    Мы предлогаем создать проект с нуля, это позволит лучше понять как работает и строится ASP.NET MVC проект, к тому же мне не нравится демо код, который присутствует при стандартном создании проекта.


    Начнем с чистого листа


    Начнем с того, что создадим проект Class Library для C#. Теперь удалим с него все лишнее: Class1.cs, AssemblyInfo.cs с директории Properties и все что есть в References. В итоге получим просто пустой проект.

    Типы проектов


    Все мы знаем, что Visual Studio поддерживает множество разных типов проектов, с каждым из которых студия работает по разному. К примеру, если мы попытаемся запустить созданный нами Class Library project (Ctrl-F5), то получим следующее сообщение:

    A project with an Output Type of Class Library cannot be started directly.

    Поскольку нашей целью является создание ASP.NET MVC проекта, то изменим тип нашего проекта соответственно. Для этого выбираем пункт Unload Project с контекстного меню проекта, откроем файл проекта на редактирование (правой кнопкой по проекту — Edit) и добавляем Guid для проектов типа Class Library:

    <ProjectTypeGuids>{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

    А теперь добавим Guid для Web Application:

    <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

    Обратите внимание на то, что Guid для Web Application должен обязательно быть первым, иначе VS не перезагрузит проект.

    После того как мы перезагрузим проект (правой кнопкой по файлу проекта — Reload Project), мы видим, что иконка проекта изменилась. Если теперь попробовать запустить проект, то студия запустит эксплорер. Кроме того, поскольку для нормальной работы Web Application нужны дополнительные настройки, то после перезагрузки проекта студия подкорректирует секцию конфигураций ProjectExtensions.

    Завершающим этапом в изменении типа нашего проекта будет корректировка Output path проекта на bin \. Для этого в свойствах проекта перейдем на вкладку Build и изменим Output path.
    image

    Настройка маршрутизации


    В ASP.NET MVC, как и в большинстве других MVC фреймворков, URL не отображают фактический путь к файлу на диске, как это реализовано в ASP.NET WebForms. В MVC урл имеет более интуитивно понятный вид [контроллер/действие], это преобразование называется routing и для того чтобы оно работало корректно — нам нужно внести некоторые изменения в проект.

    Прежде всего добавим Web.config. Очистим его и добавим следующее:

    <httpModules>
      <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    </httpModules>


    Этот модуль нужен для проверки соответствий запрашиваемой URL с заданными маршрутами и если соответствие найдено, HttpHandler берет управление на себя.

    Создадим наше первое приложение


    Определение маршрутизации лучше всего выполнять на старте приложения (Application_Start). Для этого нам нужно добавить в проект Global Application Class(Global.asax файл). Я предпочитаю заменять файл Global.asax.cs (codebehind file) на свой Application.cs.
    Получаем следующий Global.asax:

    <%@ Application Inherits="Mvc.Application" %>

    Определение маршрутов


    После того, как мы добавили Global Application Class, Visual Studio добавила несколько ссылок (references) к проекту:
    image

    Добавим еще ссылку на сборку System.Web.Routing. (Эта сборка устанавливается с NET 3.5 Service Pack 1)
    Далее добавляем вызов метода RegisterRoutes в Application_Start:

    using System.Web;

    namespace Mvc
    {
      public class Application : HttpApplication
      {
        public void Application_Start()
        {
          Router.RegisterRoutes(RouteTable.Routes);
        }
      }
    }


    Для определение роутов я использую класс Router, который мы определим позднее. Обратите внимание на RouteTable.Routes — это коллекция типа RouteCollection, которая будет содержать все маршруты нашего приложения, а метод RegisterRoutes заполняет эту коллекцию данными. Сам маршрут(route) содержит в себе информацию о том, каким образом нужно обрабатывать запрос.

    routes.MapRoute(
     "Default",            // Route name
     "{controller}/{action}/{id}",       // URL with parameters
     new { controller = "Home", action = "Index", id = "" } // Parameter defaults
     );


    UrlRoutingModule сопоставляет текущий запрос с определенными нами маршрутами и обрабатывает его соответствующим образом. Если запрос соответствует одному из заданных нами маршрутов, то RouteHandler передает управление HttpHandler.

    Теперь, добавим ссылку на сборку System.Web.Mvc и определим сам класс Router. Класс Router будет иметь вид:

    using System.Web.Mvc;
    using System.Web.Routing;

    namespace Mvc
    {
      public static class Router
      {
        public static void RegisterRoutes(RouteCollection routes)
        {
          Route route = new Route("{controller}/{action}", new MvcRouteHandler());
          routes.Add(route);
        }
      }
    }


    MvcRouteHandler создаст экземпляр MvcHandler, который обрабатывает запросы, базируясь на данных с RequestContext.
    Маршрут (Route) должен содержать информацию о том, какой контроллер использовать (инстанциировать) и какой метод этого контроллера вызывать.


    Добавление контроллеров


    Добавим контроллер в директорию Controllers, которая обычно содержит все контроллеры:

    using System.Web.Mvc;

    namespace Mvc.Controllers
    {
      public class HomeController : Controller
      {
        public string Index()
        {
          return "Hello World!";
        }
      }
    }


    В этом контроллере определен один метод Index(). Теперь, когда мы перейдем по адресу ~/home/indeх, мы увидим надпись «Hello World!».

    Далее модифицируем метод RegisterRoutes следующим образом:

    public static void RegisterRoutes(RouteCollection routes)
    {
      Route route = new Route(
        "{controller}/{action}",
        new RouteValueDictionary(new { Controller = "Home", Action = "Index" }),
        new MvcRouteHandler());
      routes.Add("Default", route);
    }


    Фактически, мы сделали так, что метод Index, контроллера Home будет выполняться по умолчанию.То есть, если мы перейдем по адресу ~/ то мы увидим результат выполнения Index().

    Добавление вида


    Поскольку, возвращать всю страницу в виде строки, далеко не самая лучшая практика построения веб приложения, то мы добавим представление, которое будет содержать в себе дизайн страницы.

    Создадим следующую иерархию папок Views/Home и добавим новый GUID, который укажет студии, что мы работаем с MVC проектом:

    <ProjectTypeGuids>{603c0e0b-db56-11dc-be95-000d561079b0};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>


    После перезагрузки проекта, нам станут доступны новые типы элементов, специфических для MVC
    image

    Представления и компоненты (control) наследуют System.Web.Mvc( вы можете убедиться в этом, посмотрев в директиву <%@ Page%>). WebFormsViewEngine использует тот же принцип компиляции, что и в WebForms. То есть, для корректной работы, нам нужно добавить ссылку на эту сборку в web.config:

    <compilation debug="true">
      <assemblies>
        <add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </assemblies>


    Далее добавим MVC View Page Index.aspx в директорию Views/Home и изменим тип, возвращаемый методом Index():

    public ActionResult Index()
    {
      return View();
    }


    Последнее, что нам нужно сделать — предотвратить прямое обращение к представлениям, которые лежат в папке Views. (Перейдите по адресу ~/views/home/index.aspx и вы увидите контент этой страницы.) Для предотвращения такой возможности добавим следующие строки в Web.config

    <?xml version="1.0"?>
    <configuration>
      <system.web>
        <authorization>
          <deny users="*"/>
        </authorization>
      </system.web>
    </configuration>


    Импортирование пространств имен в сборку


    Так же, как и WebForms, ASP.NET MVC представления компилируются в отдельном процессе aspnet_compiler. Пока мы не добавим ссылки на сборки, которые мы используем в приложении, в конфигурационный фал веб-приложения, компилятор не будет знать какие именно использовать.

    <compilation debug="true">
      <assemblies>
        <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>    
        <add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </assemblies>
    </compilation>


    Теперь компилятор будет адекватно реагировать на наши попытки использовать типы, определенные в одной из этих сборок. Теперь, для удобства, добавим пространства имен в Web.config, что позволит нам использовать короткие имена типов.

    <pages>
      <namespaces>
        <add namespace="System.Collections.Generic"/>
        <add namespace="System.Linq"/>    
        <add namespace="System.Web.Mvc"/>
        <add namespace="System.Web.Mvc.Html"/>
      </namespaces>
    </pages>


    В последнюю версию C# было добавлено множество новых конструкций, и нам конечно же хотелось бы использовать их в наших представлениях. Для этого добавим еще несколько конфигураций:

    <system.codedom>
      <compilers>
        <compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
          <providerOption name="CompilerVersion" value="v3.5"/>
          <providerOption name="WarnAsError" value="false"/>
        </compiler>
      </compilers>
    </system.codedom>


    Обобщенные виды


    В ASP.NET MVC есть возможность определять строго-типизированные представления, определив атрибут Inherits директивы Page используя обобщенный класс System.Web.Mvc.ViewPage.

    Пример:

    <%@ Page Inherits="System.Web.Mvc.ViewPage<Object>" %>

    Чтобы компилятор мог понять такую конструкцию, нам нужно определить парсер в Web.config:

    <pages pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      //...
    <pages>


    Вот и все пока что.


    Мы вручную создали ASP.NET MVC проект с минимальным набором элементов, чтобы лучше понять внутреннею структуру(работу) MVC.
    Исходники можно скачать тут.

    В следующий раз планируем написать статью о принцпах и методах, которые лучше применять для работы с ASP.NET MVC.
    Так что продолжение следует.

    P.S. Ах да, если у кого есть один лишний инвайт, то мой друг, с которым мы занимались переводом и оформлением данной статьи, тоже хотел бы попасть на хабр.
    В качестве основы бралась данная статья.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 20

      0
      Круто. Ну раз такая тема пошла, то я тоже скоро буду писать на эту тему. Может отдельный блог заведем?
      +9
      Хорошо написано, только вот я одного не пойму — зачем городить огород с правкой файла проекта и прочими телодвижениями, если в VS можно сразу создать MVC project?
        +3
        Там же сказано в конце — «чтобы лучше понять внутреннею структуру(работу) MVC»
          0
          Может, это вначале написать?)
            +3
            В начале тоже есть, но эт не столь важно. Без проблем, можно создать готовый проэкт и удалить с него весь демо код, но это ничему не научит. Я, например, лучше запоминаю руками, т.е. когда читаю какую-то книгу набираю все примеры руками, эт помогает лучше все запомнить.
              +1
              Как бы так сказать — правка файла проекта IDE != исследование внутренностей фреймворка. В крайне экстремальном случае csc.exe и notepad.exe — вот и все, что требуется.

              В общем и целом — весьма интересно и познавательно.
          • UFO just landed and posted this here
              +1
              Каждый осваивает новый материал по своему. Кому то проще книги читать, кому то скринкасты смотреть, кому то видео-уроки лучше. Мне, например, так более понятно.
          0
          Полезный материал.

          Да, исправьте, пожалуйста — проект.
            0
            Спасибо, а можно вопрос.
            Допустим я использую фреймворк типа ZF на PHP.
            Как я понимаю, то при переходе на ASP.NET MVC я получу то же самое, но от MS и для другого языка? Уж больно все похоже. И поэтому смысла нет изучать его, хоть и очень похожи. Хочу услышать чужое мнение.
              +1
              Тут не столько другой язык, как возможность использовать весь .net framework
                +1
                ASP.NET MVC по сути является альтернативой ASP.NET Web Forms, что впринципе разрешает использовать и то и другое в рамках одного проэкта. Плюс также дает возможность использовать все языки # и .NET.
                  0
                  Главное преимущество — использование ASP.NET и вообще .NET в целом и при этом создавать проект в стиле фреймворка Ruby On Rails, используя современные подходы, TDD, и прочее. Без дополнительной абстрактной надстройки в виде ASP.NET WebForms.
                  0
                  а возможно перенести исходники в более подходящее место, напр sourceforge?
                    +2
                    Вы пропустили достаточно важные абзацы при переводе оригинального текста, например о том каким образом при запросе вида ~/home/indeх из всех контроллеров приложения выбирается именно класс HomeController:

                    Once the MvcHandler has extracted the name of the controller from the route data it will ask a ControllerFactory to instantiate a controller. The DefaultControllerFactory will first determine the type of the controller. The type of the controller is ultimately determined by the ControllerTypeCache which has a list of all public, non-abstract classes that implement IController with a name ending in Controller. It is customary to put our controller classes in a folder called Controllers.

                    Вкратце это означает, что классы-контроллеры должны обязательно заканчиваться на слово Controller и в таком случае все что идет до суффикса Controller будет использоваться как имя контроллера при маршрутизации.
                    Если в Вашем примере переименовать класс HomeController просто в Home, то приложение не сработает — мне кажется это достаточно нетривиально, особенно для новичка.

                    Но в целом спасибо за статью.
                      +1
                      Отлично подмечено. По-моему самый удачный вариант роутинга это у Django: просто словарь Regex — Controller.
                      0
                      public void Application_Start()
                      {
                        Router.RegisterRoutes(RouteTable.Routes);
                      }


                      тщательно вписывали «Route» в строке «Router.RegisterRoutes(RouteTable.Routes);» чтоб не забыть что здесь роутинг?? или так работает лучше ;)
                        +1
                        Вообще если честно то скудненько написано.

                        Удалим это, а тут пропишем так то. А еще вот это добавим сюда, а этот файлик сюда.

                        Не помешало бы поподробнее описывать действий и назначения подключаемых/создаваемых файлов проекта. Особенно если эти статьи несут освоение материала, действительно с самого начала.

                        Only users with full accounts can post comments. Log in, please.