(MVC) MVC (2015 год)

Додаток про загальний код у проектах MVC.

На цієї сторінці я хотів би додати пару методів розміщення загального коду, які я детально проаналізував на своей сторінці від 2008-року - Базовые странички ASP.NET - але тепер вже у проектах MVC. Тобто як причепити свій код, який би виконувався на кожної сторінці проекту MVC?


Ось перший засіб.

У класі System.Web.Mvc.Controller е дуже корисний та цікавий зворотній виклик OnActionExecuting - без якого я не знайшов можливості обійтися в жодному своєму проєкті на MVC. Ось, наприклад, одна сторінка від проєктів вотпуска у мене опублікована тут - Проекты нового Вотпуска. Там цей засіб розміщення загального коду використовується для утворення деякої структури даних TxtNodes - яка потім використовується для побудування навігації по сторінках сайту.

А тут я покажу про побудування за допомогою цього засобу контексту виконання поточного юзеру, тобто по загальному призначенню це дуже схоже за тим, що у мене описано для проєкта http://arenda.votpusk.ru/ на сторінці Базовые странички ASP.NET.

Отже, код контролерів побудований якось так:


   1:  Namespace OCMR
   2:      Public Class AdminController
   3:          Inherits System.Web.Mvc.Controller
   4:   
   5:          Dim CurrentUser As Global.OCMR.User
   6:   
   7:          Protected Overrides Sub OnActionExecuting(ctx As System.Web.Mvc.ActionExecutingContext)
   8:              MyBase.OnActionExecuting(ctx)
   9:              CurrentUser = CabAu.GetCurrentUser
  10:              If CurrentUser Is Nothing Then ctx.HttpContext.Response.RedirectPermanent("/Home/Index")
  11:          End Sub
  12:   
  13:          Function Index() As ActionResult
  14:              Return View()
  15:          End Function
  16:   
  17:  .....
  18:   
  19:          <HttpPost()> _
  20:          Function Logout(Prm As FormCollection) As ActionResult
  21:              FormsAuthentication.SignOut()
  22:              Return RedirectToActionPermanent("Index", "Home")
  23:          End Function
  24:   
  25:      End Class
  26:  End Namespace

І у цьому контроллері CurrentUser - це завжди поточний юзер:


   1:  Public Class CabAu
   2:   
   3:      Public Shared Function GetCurrentUser() As Global.OCMR.User
   4:   
   5:          If Not HttpContext.Current.User.Identity.IsAuthenticated Then Return Nothing
   6:          Dim db1 As New OCMRDataContext
   7:          Dim CurUser = (From X In db1.Users Select X Where X.Email.Trim = HttpContext.Current.User.Identity.Name).ToList
   8:          If CurUser.Count = 0 Then
   9:              Return Nothing
  10:          Else
  11:              Return CurUser(0)
  12:          End If
  13:      End Function
  14:   
  15:  End Class

Як бачите, це дуже зручно! Особливо у маленьких проєктах, де взагалі усіх юзерів можно взяти у кеш і взагалі не торкатися до бази щоб знайти контекст поточного юзеру. Крім того, як бачите, це автоматично захищає код контролеру від анонімів.




І тепер другий цікавий засіб, якого теж не було раніше у проєктах ASP.NET - який з'явився лише у ASP.NET MVC. У проектах ASP.NET необхідності у цьому засобі не було, так як студія автоматично при утворенні Master Page утворювала класс з кодом, на якому можна було помістити загальний код (це четвертий описаний мною засіб на цієї сторінці Базовые странички ASP.NET.

У проєктах MVC такий класс з кодом треба утворювати самому (вручну). Для цього потрібно у першій стрічці Master Page зробити наслідок не від мікрософтовского класу System.Web.Mvc.ViewMasterPage а від свого классу (у данному випадку OCMR.MasterShared):


   1:  <%@ Master Language="VB" Inherits="OCMR.MasterShared" %>
   2:   
   3:  <!DOCTYPE html>
   4:  <html>
....
  51:          <div class="main">
  52:              <asp:ContentPlaceHolder ID="MainContent" runat="server">
  53:              </asp:ContentPlaceHolder>
  54:          </div>
....
  61:  </body>
  62:  </html>

Таким чином з'являється загальний код Master Page, який буде виконуватися на усіх сторінках:


   1:  Public Class MasterShared
   2:      Inherits System.Web.Mvc.ViewMasterPage
   3:   
   4:   
   5:      Private Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
....
  10:      End Sub
  11:   
  12:  End Class

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




І третій, неможливий у простому ASP.NET засіб - наслідування одного контроллеру від іншого. З якоїсь точки зору увесь ASP.NET - це один контроллер, а якщо з'являється декілька контролерів, то навіщо ж розміщати загальний код проєкту у кожному?

Спочатку я поясню загальну постанову задачі, у якій цей метод можливо застосувати. Ось, наприклад я маю проект, у якому є звичайний юзер, потім редактор, який має трошки більше повноважень, потім супервізор, який має ще більше повноважень. Пункти меню цих користувачив сайту виглядають ось так:





Як бачите, супервізор має усі функції редактора, а редактор має усі функції звичайного юзера. Але "у лоб" зробити наслідування одного контролера від іншого не вийде, так працює System.Web.Mvc.WebFormViewEngine - навіть коли я явним чином задаю контролер, якщо System.Web.Mvc.WebFormViewEngine побачить однакові методи у обох контролерах, він видасть помилку:



Можливо навіть зробити власній якось так, але й це не врятує. Тому метод повинен буде один на всі контролери проекту, тобто у данному випадку метод EULA повинен присутній тільки в контролері Home, а у контролерах Admin та Supervisor повинні бути лише додаткові MVC-методи.



Зрозуміло, що стандартний механізм пошуку VIEW потребує, щоб VIEW знайшлося або у папці з контролером, або у папці SHARED. Тоді двигун MVC зможе заштовхнути дані, які видав контролер у VIEW і таким чином сформувати HTML, як це у мене описано на сторінці - Кешування вхідної сторінки сайту.

Але справа в тому, що у мене є аж чотири MasterPage, і MasterPage трошки відрізняються у кожного типу юзера, наприклад загальним фоном та підвалом сайту. Тому для одного ж того метода (наприклад СROP), у кожному контролері мені потрібно перемикати MasterPage.



Зробити це можливо ось таким кодом:


   1:  Namespace OCMR
   2:   
   3:      <NoCache()>
   4:      Public Class CabController
   5:          Inherits System.Web.Mvc.Controller
   6:   
   7:          Public MasterName As String
   8:          Public RefName As String
   9:   
  10:          Protected Overrides Sub OnActionExecuting(ctx As System.Web.Mvc.ActionExecutingContext)
  11:  ...
  12:              If CurrentUser Is Nothing Then ctx.HttpContext.Response.Redirect("/Home/Index")
  13:              Select Case ControllerContext.Controller.GetType().Name
  14:                  Case "CabController"
  15:                      MasterName = "MC"
  16:                  Case "AdminController"
  17:                      MasterName = "MA"
  18:                  Case "SupervisorController"
  19:                      MasterName = "MS"
  20:                  Case Else
  21:                      MasterName = "M1"
  22:              End Select
  23:              If Request.UrlReferrer IsNot Nothing Then
  24:                  If Request.UrlReferrer.AbsolutePath.Contains("Supervisor") Then
  25:                      RefName = "Supervisor"
  26:                  ElseIf Request.UrlReferrer.AbsolutePath.Contains("Admin") Then
  27:                      RefName = "Admin"
  28:                  ElseIf Request.UrlReferrer.AbsolutePath.Contains("Cab") Then
  29:                      RefName = "Cab"
  30:                  End If
  31:              End If
  32:          End Sub
  33:  ....

Тобто тут у полях MasterName та RefName буде зберігатися ім'я контролеру. І якщо необхідно змінювати логіку роботи метода (який розміщується лише в одному місці - у базовому класі контролеру), то логіку спільного у всіх контролерах методу можно змінювати залежно від контексту контролера ось таким чином:



More How to create Razor html-helper in VB.NET, Define common code as Function and Helpers (directly in View and in the codeBehind)




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