(MVC) MVC (2012 год)

Общий шаблон простейшего приложения на ASP NET MVC.

У меня на сайте есть много шаблонов стандартных приложений на все случаи жизни - например Шаблон Flex/Air приложений со вкладками - ViewStack, описано и несколько приложений на ASP.NET MVC, например Мой первый сайт на MVC 3 Razor - но простого стандартного приложения на ASP.NET MVC 3 у меня нет.

Поэтому я решил пополнить свой сайт описанием простого стандартного приложения - у которого на главной страничке есть меню, поисковая форма и форма логина. По моим наблюдениям именно это шаг при переходе от ASP.NET к ASP.NET MVC представляет наибольшую сложность - правильно сделать общий шаблон приложения, а дальше любой ASP.NET программист может уже программировать по более ли менее накатанной колее.

Увы, развитие ASP.NET было неожиданно остановлено микрософтом и вместо ожидаемого развития ASP.NET - работы с фреймами, несколькими тегами FORMS, новыми развитыми контролами с AJAX, поддержка развертывания в Linux, полноценные контролы с дизайн-таймом, которые бы можно было бы копировать из проекта в проект и так далее, список недостатков ASP.NET, требующих развития в следующей версии огромен - тем не менее микрософт все требования на разватие ASP.NET проигнорировал и выдал нам в Visual Studio 2010 среду пятнадцатилетней давности. Ровно такую же среду, какая у нас была в Visual InterDev (и есть сейчас в PHP и прочих убогих от рождения технологиях) - лишь немного подсластив Visual InterDev малоценным и никому не нужным URL-реврайтером и плюс маркетинговый отдел микрософта добавил перед словом MVC еще и название фирменной технологии ASP.NET (развитие которой прекратил). Эта идея возврата назад (вместо развития уникальной технологии ASP.NET вперед) не была воспринята радостно многими ASP.NET-программистами (в том числе и мною - если мне надо сделать быстро или для себя - я по прежнему делаю на ASP.NET) - но тем не менее, иногда заказы на сайты ASP.NET MVC появляется.

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


Нижеследующий пример будет приведен для стандартно сконфигурированного реврайтера:


   1:      Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
   2:          routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
   3:   
   4:          ' MapRoute takes the following parameters, in order:
   5:          ' (1) Route name
   6:          ' (2) URL with parameters
   7:          ' (3) Parameter defaults
   8:          routes.MapRoute( _
   9:              "Default", _
  10:              "{controller}/{action}/{id}", _
  11:              New With {.controller = "Home", .action = "Index", .id = UrlParameter.Optional} _
  12:          )
  13:      End Sub

ибо, как вы понимаете, в реврайрер можно занести любые правила преобразования URL адреса, и тогда наличие вот этих папок Home, Cab, Login, Find в корне сайта будет бессмысленным.

Собственно говоря, самая распространенная модификация эти правил заключается в добавлении фрагмента {area} в строке 10 - если поставить слева перед фрагментом {controller} -то все папки надо будет утопить во внутрь одной из Area.

Другой распространенной модификацией является добавления фрагмента {Login} в качестве первого или второго сегмента (так делают например социальные сети). В этом случае также структура папок и контроллеров будет другая.

Но мы рассматриваем самый стандартный минимальный вариант.


Итак, после создания проекта на ASP NET MVC 3 все начинается в Master page - на ней будут ссылки на внутренние странички сайта и ссылки на контролы логина и поиска.


   1:  <%@ Master Language="VB" Inherits="System.Web.Mvc.ViewMasterPage" %>
   2:   
   3:  <!DOCTYPE html>
   4:  <html lang="ru">
   5:  <head>
   6:  ...
   7:  </head>
   8:  <body>
   9:  ...
  10:  <a href="/home/news"       class="menu">Новости</a>
  11:  <a href="/home/company"    class="menu">О компании</a>
  12:  <a href="/home/colaborate" class="menu">Сотрудничество</a>
  13:  <a href="/home/catalog"    class="menu">Каталоги</a>
  14:  <a href="/home/delivery"   class="menu">Доставка</a>
  15:  <a href="/home/spares"     class="menu">Запчасти</a>
  16:  <a href="/home/supplier"   class="menu">Поставщикам</a>
  17:  <a href="/home/job"        class="menu">Вакансии</a>
  18:  <a href="/home/contact"    class="menu">Контакты</a>
  19:  ...
  20:  <a href="/home"><img src="../../Image/logo.png"></a>
  21:  ...
  22:  <% Html.RenderPartial("Find")%>
  23:  ...
  24:  <% Html.RenderPartial("Logon")%>
  25:  ...
  26:  <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
  27:  </asp:ContentPlaceHolder>
  28:  ...
  29:  </body>
  30:  </html>

Все внутренние странички следуют поместить во внутрь папки Home и выглядеть они будут так:


   1:  <%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/M1.Master" Inherits="System.Web.Mvc.ViewPage" %>
   2:   
   3:  <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
   4:  ...
   5:  </asp:Content>

Остановимся на секунду и призадумаемся. Каким-то странным и непостижимым образом в технологии MVC сохранились теги asp:ContentPlaceHolder и asp:Content. Это вызывает неподдельное удивление. Это пожалуй единственный комплект тегов ASP.NET, который можно использовать в MVC

Вообще в ASP.NET существует следующий набор тегов (их значительно больше - ниже приведены только TopLevel-теги) :


Но использование всех этих ASP.NET тегов на страничках ASP.NET MVC запрещено. За исключением asp:ContentPlaceHolder и asp:Content (может и еще какого-то тега?). По замыслу микрософтовцев, видимо именно использование тега asp:ContentPlaceHolder и asp:Content должно оправдывать финт ушами маркетингового отдела - добавить перед названием технологии MVC литерал ASP.NET. Такой финт ушами выглядит обидно для ASP.NET программистов - мы жаловались что нельзя откомпилировать контрол и использовать его во многих проектах, что нельзя в дизайн-тайме создать кастом-контрол, жаловались на ограниченность стандартного комплекта контролов - но Билл Гейтс нас вылечил просто - запретил использование ASP.NET контролов в MVC ПОЛНОСТЬЮ и в насмешку добавил лого ASP.NET перед названием MVC.

Зато теперь в MVC появились новые теги. Которыми надо манипулировать лапшеобразным кодом, перемешывая код языка программирования и вот эти теги (так как мы делали 15-20 лет назад в Visual InterDev до появления ASP.NET)- но почему-то не домешивая в этот венегред SQL (хотя это было бы явно полезнее). Ну не говоря уже о том, что все последние 10 лет ASP.NЕT гордились отсутствием лапши в коде и глумились над PHP-программистами (и прочими убогими) из-за лашеобразной каши у них вместо нормального кода на языке программирования.



Теги <%HTML не только неизвестны ASP.NET программистам (со всеми их параметрами и заморочками), но и предельно несовершенны, в итоге приходится пользоватся обычным HTML. Напимер если я хочу добавить в текст линка <nobr или <span - то мне придется писать весь код странички как текстовый литерал, без подсветки и подсказки. Поэтому на мастер-страничке выше вы видите как я обозначил линки.

Тем не менее, я бы хотел заметить, что все не настолько плохо в MVC. Если вы собираетесь писать сайт на AJAX - то простой ASP.NET с прикруткой к нему UpdatePanel - явно недостаточно. На мой взгляд для сайтов с вактивным клиентским функционалом в технологии MVC явно смысл есть.

Четыре нижеследующих тега умеют работать асинхронно, что не может не порадовать:



Хотя... хм... я нахожу FLEX более удобным для сайтов с активным клиентским функциналом - для сравнения оцените объем адобовского фреймворка относительно микрософтовского - это десятки тысяч классов для активного функционала странички. Но все равно радует, что у микрософта в дополнение к UpdatePanel наконец-то появились хотя бы эти четыре тега.

На этом теоретическую часть закончим. На мой страничке Мой первый сайт на MVC 3 Razor вы также можете увидеть что именно может вернуть в виде результата контроллер, а также увидеть все три вида моделей в ASP.NET MVC - простые поля, IEnumerable-коллекции, коллекции произвольных классов - Collections.Generic.List.


А теперь я покажу код четырех контроллеров, который джонглирует этими вьюшками. Итак, первый HomeController.vb просто переключает странички, ссылки на которые стоят в главном меню:


   1:  Namespace Omsk
   2:      Public Class HomeController
   3:          Inherits System.Web.Mvc.Controller
   4:   
   5:          ' GET: /Home
   6:          Function Index() As ActionResult
   7:              Return View()
   8:          End Function
   9:   
  10:          ' GET: /Home/News
  11:          Function News() As ActionResult
  12:              Return View("News")
  13:          End Function
  14:   
  15:          ' GET: /Home/Catalog
  16:          Function Catalog() As ActionResult
  17:              Return View("Catalog")
  18:          End Function
  19:   
  20:          ' GET: /Home/Colaborate
  21:          Function Colaborate() As ActionResult
  22:              Return View("Colaborate")
  23:          End Function
  24:   
  25:          ' GET: /Home/Company
  26:          Function Company() As ActionResult
  27:              Return View("Company")
  28:          End Function
  29:   
  30:          ' GET: /Home/Contact
  31:          Function Contact() As ActionResult
  32:              Return View("Contact")
  33:          End Function
  34:   
  35:          ' GET: /Home/Delivery
  36:          Function Delivery() As ActionResult
  37:              Return View("Delivery")
  38:          End Function
  39:   
  40:          ' GET: /Home/Job
  41:          Function Job() As ActionResult
  42:              Return View("Job")
  43:          End Function
  44:   
  45:          ' GET: /Home/Spares
  46:          Function Spares() As ActionResult
  47:              Return View("Spares")
  48:          End Function
  49:   
  50:          ' GET: /Home/Supplier
  51:          Function Supplier() As ActionResult
  52:              Return View("Supplier")
  53:          End Function
  54:   
  55:      End Class
  56:  End Namespace

Контрол Find.ascx выглядит вот так:


   1:  <%@ Control Language="VB" Inherits="System.Web.Mvc.ViewUserControl" %>
   2:   
   3:  <% Using Html.BeginForm("Search", "Find", Nothing, Web.Mvc.FormMethod.Post)%>
   4:  ...
   5:  <input name="search" size="25" type="text">
   6:  <input name="cross" value="1" checked="checked" type="checkbox">
   7:  <input id="SearchButton" class="postbutton" value="Найти" type="submit" />
   8:  ...
   9:  <% End Using%>

Контроллер FindController.vb - принимает Web.Mvc.FormMethod.Post от контрола Find.ascx и выглядит просто:


   1:  Namespace Omsk
   2:      Public Class FindController
   3:          Inherits System.Web.Mvc.Controller
   4:   
   5:          'Post: /Find/Search?search=&cross=1
   6:          <HttpPost()> _
   7:          Function Search(ByVal Prm As FormCollection) As ActionResult
   8:              Return View()
   9:          End Function
  10:   
  11:      End Class
  12:  End Namespace

Чуть сложнее выглядит контроллер логина LoginController.vb, потому что я обработал процесс регистрации в этом же контроллере. Чтобы не осложнять смысл заметки - я не стал здесь включать код доступа к базе (через DHTML) - а просто сравнил логин/пароль с литеральными константами.


   1:  Namespace Omsk
   2:      Public Class LoginController
   3:          Inherits System.Web.Mvc.Controller
   4:   
   5:          'сюда смотрит линк с контрола регистрации
   6:          Function GoRegister() As RedirectResult
   7:              Return Redirect("Register")
   8:          End Function
   9:   
  10:          'PageLoad вьюшки /Login/Register
  11:          Function Register(ByVal Prm As FormCollection) As ActionResult
  12:              Return View() 'View("Register") 
  13:          End Function
  14:   
  15:          'Постбек данных для регистрации
  16:          <HttpPost()> _
  17:          Function RegisterPost(ByVal Prm As FormCollection) As ActionResult
  18:              FormsAuthentication.SetAuthCookie(Prm("Login"), True)
  19:              Return View() 'View("RegisterPost")
  20:          End Function
  21:   
  22:          'сюда пришел постбек с контрола регистрации при нажатии ВОЙТИ
  23:          <HttpPost()> _
  24:          Function Post(ByVal Prm As FormCollection) As RedirectResult
  25:              If Prm("Login") = "1" And Prm("Password") = "1" Then
  26:                  FormsAuthentication.SetAuthCookie(Prm("Login"), True)
  27:                  'залогинился
  28:                  Return Redirect("/Cab")
  29:              Else
  30:                  'не залогинился
  31:                  Return Redirect("/home")
  32:              End If
  33:   
  34:          End Function
  35:   
  36:          'постбек разлогинивания (с контрола)
  37:          <HttpPost()> _
  38:          Function UnLogin(ByVal Prm As FormCollection) As RedirectResult
  39:              FormsAuthentication.SignOut()
  40:              Return Redirect("/home")
  41:          End Function
  42:      End Class
  43:  End Namespace

Этот контроллер жонглирует контролом Logon.ascx :


   1:  <%@ Control Language="VB" Inherits="System.Web.Mvc.ViewUserControl" %>
   2:   
   3:  <% If Request.IsAuthenticated Then%>
   4:   
   5:  <% Using Html.BeginForm("UnLogin", "Login", Nothing, Mvc.FormMethod.Post)%>
   6:  ...
   7:  <%: Html.Label(Request.RequestContext.HttpContext.User.Identity.Name)%></span>
   8:  <input id="Submit1" class="postbutton" value="Выйти" type="submit" />
   9:  ...
  10:  <%   End Using%>
  11:   
  12:  <% Else%>
  13:   
  14:  <% Using Html.BeginForm("Post", "Login", Nothing, Mvc.FormMethod.Post)%>
  15:   
  16:  Логин:<input name="login" style="width: 163px;" type="text">
  17:  Пароль:<input name="password" style="width: 163px;" type="password">
  18:  ...
  19:  <input id="register" class="postbutton" value="Регистрация" type="button" />
  20:  ...
  21:  <input id="login" class="postbutton" value="Войти" type="submit" />
  22:  ...
  23:   
  24:  <%   End Using%>
  25:   
  26:  <% End If%>

И поскольку я обединил тут логин и регистрацию в одном контроллере, то и вьюшкой Register.aspx:


   1:  <%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/M1.Master" Inherits="System.Web.Mvc.ViewPage" %>
   2:   
   3:  <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
   4:   
   5:  Регистрация
   6:   
   7:  <% Using Html.BeginForm("RegisterPost", "Login", nothing ,Mvc.FormMethod.Post)%>
   8:  ...
   9:  Логин:   <input name="login"    type="text">
  10:  Пароль:  <input name="password" type="password">
  11:  Email:   <input name="email"    type="text">
  12:  Имя:     <input name="name1"    type="text">
  13:  Фамилия: <input name="name2"    type="text">
  14:  ...
  15:  <input id="register" class="postbutton"  value="Зарегистрироватся"    type="submit" />
  16:  ...
  17:  <%   End Using%>
  18:   
  19:  </asp:Content>

И вьюшкой RegisterPost.aspx:


   1:  <%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/M1.Master" Inherits="System.Web.Mvc.ViewPage" %>
   2:   
   3:  <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
   4:  Вы успешно зарегистрированы
   5:   
   6:  <% Using Html.BeginForm("RegisterPost", "Login", Nothing, Mvc.FormMethod.Post)%>
   7:          <a href="/cab">
   8:          <input id="register" class="postbutton" value="перейти в кабинет" type="button" /></a>
   9:  <%End Using%>
  10:   
  11:  </asp:Content>

В целом такой простой и универсальный шаблон много раз повторяется на простейших сайтах - потом на этот простейший MVC-каркас натягивается шуба из HTML - и даже такие простейшие сайты выглядят вполне прилично.



Comments ( )
Link to this page: //www.vb-net.com/MVC_CommonTemplate/index.htm
< THANKS ME>