Razor Pages — новая фича, появившаяся в Core.Net 2.0. Razor Page — это страница, состоящая из стандартной разметки (View) и бэкенд класса. В каком то смысле напоминает Web Forms только без поддержки сохранения состояния. Преимущество такого решения очевидно — мы избавляемся от ненужной прослойки — модели страницы (модель данных в виде например Entity это само собой). Бэкенд страницы является и контроллером и моделью — классика ООП — инкапсуляция данных и методов работы с ними в одном объекте. В конце концов модель страницы — это просто класс, нет никаких причин почему этим классом не может быть контроллер.

Иными словами, Razor Pages — более вменяемое решение для веба чем MVC, теперь мы имеем дело с традиционным и логичным понятием «страница» а не с контролерами и моделями разбросанными по всему проекту. Но поскольку .NET будет развиваться по направлению Core.Net то вряд ли Razor Page появятся в стандартном фреймворке, несмотря на то что ближайшие годы большинство проектов будет оставаться на стандартном .NET. Тем не менее можно изобразить функциональность Razor Pages и на стандартном фреймворке.

Решение на самом деле выглядит достаточно тривиально — нужно добавить в контролер следующую конструкцию:

protected override void OnActionExecuting(ActionExecutingContext Context)
{
       UpdateModel(this);
}

Метод OnActionExecuting -событие жизненного цикла который вызывается перед выполнением метода контролера (имеется ввиду обработчик запроса — Action).

UpdateModel выполняет непосредственно привязку параметров запроса к свойствам (properties) модели — в данном случае свойствам класса контроллера.

Дополнительное удобство — теперь нет необходимости вообще принимать явно параметры ни типа Model ни какие другие. Хотя ничего не мешает это делать, если параметр — простой id, который будет использовать чисто как локальная переменная, но привязка параметров как свойств контролера необходима например, если нужно обеспечить персистентность страницы, о чем пойдет речь дальше.

Простой пример:

У нас есть обычная форма логина с двумя полями.

Приводить разметку нет смысла приведу только код контроллера

 public class AccountController : Controller
 {
     public string username{ get; set; }
     public string userpass{ get; set; }

     [HttpPost]
     public ActionResult OnLogin( )
     {
              //некая функция проверки в БД
               checklogin(username,userpass);

               return View("Index",this);
     }

     protected override void OnActionExecuting(ActionExecutingContext Context)
    {
   
            UpdateModel(this);
     }
 }


Как видим, в момент срабатывания события входные данные уже привязаны и готовы к использованию.

Разумеется, надо иметь ввиду что теперь возвращать как ActionResult тоже нужно контроллер ну и в шаблоне прописать имя класса контролера — типа @Model AccountController.

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

Вы кликаете по фильтру модель возвращается в представление и все хорошо, но когда кликните сортировку фильтр естественно сбросится. Пагинатор сбросит и сортировку и фильтр. В WebForms состояние страницы сохранялось автоматически, в MVC приходится применять разные громоздкие решения, например склеивать все параметры и гонять их по каждому запросу, то есть на ссылку сортировки нужно повесить все параметры которые пришли перед этим с фильтра.

Подобные затруднения являются одной из причин SPA и прочего яваскриптового безумия с перетаскиванием логики и данных в браузер с вытекающими из этого тормозами браузера (особенно мобильных девайсах), прыгающими и дергающимися за каждым движением мышки страницами и увеличением трудоемкости и стоимости работ — ведь бэкенд то все равно писать в том или ином виде, упаковывать распаковывать данные пересылаемые через ajax, плюс callbac hell, усложнения отладки и так далее, и все это без практической пользы для посетителей сайта которым нет дела как написана страница.

Наиболее логичным решением является сохранение состояния страницы в сессии. Поскольку у нас все необходимые параметры уже под руками в виде свойств контроллера, все что нужно — это переопределить метод OnActionExecuted, который вызывается после обработки запроса, и упаковать нужные свойства в сессию (ключ сессии очевидно должен указывать на имя контролера).

protected override void OnActionExecuted (ActionExecutedContext Context)
{
      //сохраняем данные
}

Восстанавливаются параметры из сессии в конструкторе контролера или перед вызовом UpdateModel(this). Когда придет запрос, например сортировки то новые параметры изменятся а остальное останется нетронутым и представление будет отображено в том же виде как было отправлено.

Такое решение имеет еще одно удобство — к примеру, пользователь отсортировал таблицу, и решил какой то элемент отредактировать, открывши для этого другую страницу. Естественно он хочет вернутся в то состояние списка которое оставил а так как состояние страницы у нас в сессии, то страница восстановится автоматически. Нет никакой необходимости, как часто делают, передавать весь «вареник» параметров в страницу редактирования и обратно. Если сохранять состояние между страницами нет необходимости то состояние страницы можно хранить не в сессии а в TempData.

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

Комментарии (31)


  1. mayorovp
    27.08.2019 14:09

    Razor Pages — новая фича, появившаяся в Core.Net 2.0

    Нет, это довольно старая фича (известная как ASP.NET WebPages), просто её наконец-то вытащили на свет.


    Иными словами, Razor Pages — более вменяемое решение для веба чем MVC

    Больше похоже на устаревший подход а-ля PHP, который уже даже в самом PHP не рекомендуется к использованию.


    Впрочем, достоинства у него тоже есть, в узком случае страниц только для чтения.


    1. caballero Автор
      27.08.2019 14:22

      Впрочем, достоинства у него тоже есть, в узком случае страниц только для чтения.

      а в чем недостатки если страница для записи?


      1. mayorovp
        27.08.2019 14:46

        В том, что либо получается куча логики среди разметки, либо же от контроллера не удаётся избавиться.


        1. caballero Автор
          27.08.2019 14:57
          -2

          откуда куча логики в разметке? Разметка тут вообще не меняется.
          И зачем избавляться от контроллера? Мы избавляемся не от контроллера а от ненужной прослойки под названием модель страницы. По сути это то что иногда называют DTO (Data Transfer Object) — обычно это костыль подпирающий неудачную архитектуру.
          А контроллер превращается в бекенд страницы соответствующий парадигме ООП и просто здравому смыслу.
          Я так понимаю вы из поколения программистов считающих что MVC и веб это одно и тоже. Но лично мой мой многолетний опыт програмирования как раз убедил меня в обратном — MVC (в той релизации в которой он применяется в большинстве фреймворков) — самый неудачный паттерн для веба.


          1. mayorovp
            27.08.2019 15:03

            И зачем избавляться от контроллера?

            Потому что Razor Pages и Razor Views, в том числе, отличаются наличием контроллера, и именно это отличие заметнее всего.


            Если вы пытаетесь избавиться от "модели страницы" — вы решаете какую-то другую задачу, отличную от декларированной.


            И да, объединить контроллер с "моделью страницы" (на самом деле моделью запроса) — совершенно не то же самое что от модели избавиться.


            Я так понимаю вы из поколения программистов считающих что MVC и веб это одно и тоже.

            Судя по тому что я вижу — это как раз вы так считаете...


            1. caballero Автор
              27.08.2019 15:16

              Потому что Razor Pages и Razor Views, в том числе, отличаются наличием контроллера, и именно это отличие заметнее всего.

              смысл в данном случае — реализовать аналогичный функционал. как будет называтся бекенд и в какой папке он будет не суть важно.
              И да, объединить контроллер с «моделью страницы» (на самом деле моделью запроса) — совершенно не то же самое что от модели избавиться.

              мы избавляемся от лишнего класса — посредника, чем упрощаем код и делаем его более прозрачным. Нет ни одного разумного аргумента (кроме тупого минусования) зачем нужен класс смысл которого переложить данные с одного места в другое в пределах одного приложения.


    1. codecity
      27.08.2019 15:24

      Нет, это довольно старая фича (известная как ASP.NET WebPages), просто её наконец-то вытащили на свет.

      Совсем не то. В Razor Pages во View работа с моделью, основные методы (код) в отдельном C#-классе. Т.е. присутствует разделение представления и логики.

      Основная претензия к тем же JSP, старому ASP, PHP (хотя PHP и другие проблемы, более серьзеные) — это мешанина из кода и разметки. В Razor Pages эта проблема решена.


      1. mayorovp
        27.08.2019 16:04

        Там "модель" такая, что больше на CodeBehind страницы похожа...


        1. codecity
          27.08.2019 17:18

          В чем практический минус?


          1. mayorovp
            27.08.2019 17:22

            Тут не то чтобы минус, просто я как-то не вижу особых преимуществ.


            1. codecity
              27.08.2019 17:25

              Как по мне — удобно что контроллер рядышком с View. Не нужно его искать в другой папке.

              Модель-контроллер в Razor Pages объединяет как данные, так и методы работы с ними. Ближе к классике ООП, но есть и свои минусы.


              1. mayorovp
                27.08.2019 17:38

                Так-то и в MVC вовсе не обязательно было придерживаться структуры папок по умолчанию.


      1. caballero Автор
        27.08.2019 16:17

        присутствует разделение представления и логики.

        в MVC оно тоже присутствует только логика страницы размазывается по разным классам.
        А вот в самом razor движке как раз с таким разделением не очень. начиная от запутаного синтаксиса (не сразу сообразишь где надо ставить @ а где нет) до разного рода Html хелперов которые затрудняют работу верстальщикам. Но это отдельная проблема самого движка как активном шаблонизатора а значит неизбежно содержащего код вперемешку с версткой. В этом плане Razor ничем не лучше PHP и JSP


        1. codecity
          27.08.2019 17:21

          в MVC оно тоже присутствует только логика страницы размазывается по разным классам

          В MVC тратится время, чтобы переходить от View к нужному контроллеру (т.е. навигация в процессе разработки). А в Razor Pages все рядышком…

          Совмещение данных и методов, которые с ними работают (т.е. объединение модели и контроллера в один класс) — имеет как плюсы так и минусы. В любом случае подход имеет право на жизнь. Главное что View — отдельно.


  1. mayorovp
    27.08.2019 14:14

    Теперь по вашей реализации.


    Решение на самом деле выглядит достаточно тривиально — нужно добавить в контролер следующую конструкцию

    Откуда у нас вообще взялся контроллер, если у нас, вообще-то, Razor Pages?


    Наиболее логичным решением является сохранение состояния страницы в сессии.

    Но это сразу же делает невозможным работу в двух окнах браузера!


    Почему бы просто не добавить на страницу нужные скрытые поля?


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

    Это вообще не проблема. Это решение как раз и является "родным" для веба.


    1. caballero Автор
      27.08.2019 14:35

      Откуда у нас вообще взялся контроллер, если у нас, вообще-то, Razor Pages?

      Razor Pages это в Core. Суть в том как это сделать в стандартном ASP.NET MVC фреймворке. При желании конечно можно убрать контроллер — то есть переименовать его и может даже переместить, но смысла особого в этом нет.
      Но это сразу же делает невозможным работу в двух окнах браузера!

      не думаю что люди часто работают с одной и той же страницей в двух окнах (тем более это чревато работой с неконсистентными данными). Обычно работают с РАЗНЫМИ страницами одного и того же сайта.

      Почему бы просто не добавить на страницу нужные скрытые поля?

      потому что не все запросы отправляют форму. Это в WebForms вся страница бла формой и все запросы были POST
      Это решение как раз и является «родным» для веба.

      Нет — родным для веба является как раз stateless то есть проблема, а решения бывают разные. Согласен, в большинстве случаев тупо гоняют вереницу параметров.
      Об это и речь, что можно делать и по другому, резко упростивши количество кода.


      1. mayorovp
        27.08.2019 14:48
        +1

        Razor Pages это в Core. Суть в том как это сделать в стандартном ASP.NET MVC фреймворке.

        А зачем вы ограничиваете себя MVC фреймворком? Напомню, что Razor Pages, которые в Core, с MVC никак не связаны.


        1. caballero Автор
          27.08.2019 15:04

          Ну технически Razor Pageв Core.NET строится поверх MVC, поэтому обязательно требуется использовать useMvc в Startup.cs

          Но суть как раз в том чтобы упростить стандартный (не Core.NET а обычный .NET) MVC фреймворк где RAzor Page нет и вряд ли будет.. Потому что на нем пишут 99 из ста приложений. Не MVC решения очевидно не обладают недостатками которые исправляет решение аналогичное Razor Pages


      1. Kanut
        27.08.2019 22:05

        Суть в том как это сделать в стандартном ASP.NET MVC фреймворке. При желании конечно можно убрать контроллер — то есть переименовать его и может даже переместить, но смысла особого в этом нет.

        Суть в том что по большому счёту смысла нет во всей вашей идее. То есть прикольно поиграться и выяснить что да, оно действительно так можно сделать. Но вещи в продакшен я бы на таком никогда писать не стал.

        И если вас не устраивает MVC, что я очень даже понимаю, то наверное стоит посмотреть на имеющиеся и/или запланированные на будущее альтернативы, а не пытаться изобретать непонятно что…


        1. caballero Автор
          28.08.2019 05:57

          а какие проблемы с этим в продакшене? И это не моя идея — Razor Pages в Core не я добавил. Я просто показал как можно это повторить в стандартном фреймворке.
          В этом и преимущество разработки сообществом в опенсорсе коим ща является Core.Net — там не только те кому на курсах программирование рассказали что MVC это священная корова и по другому програмировать не моги или разрабы мелкомягких которым наплевать как на юзеров так и на других разрабов которые этим будут пользоватся.

          на имеющиеся и/или запланированные на будущее альтернативы

          Razor Pages и есть альтернатива на данный момент и пока я не услышал ТЕХНИЧЕСКИ обоснованных аргументов почему это будет работать хуже MVC. Компилятор сгенерит почти тот же код — удобства тут именно с точки зрения разработки — меньше индусского кода.

          .


          1. Kanut
            28.08.2019 08:52
            +1

            Во первых не надо всех налево и направо обвинять в том что они считают MVC «священной коровой» и не могут себе представить веб без MVC.
            Я MVC сам не особо люблю и потратил много усилий чтобы убедить начальство его больше не использовать в наших проектах.
            Но всё равно надо признать что MVC это известный паттерн, а .Net MVC известная технология, которая легко понятна любому джуну и вполне себе имеет свои области применения.

            Во вторых если вам нравятся Razor Pages и вы считаете что именно за ними будущее, то переходите на эти самые Razor Pages. Но вы вместо этого пытаетесь «перекрутить» MVC так, чтобы он стал похож на Razor Pages. И именно это на мой взгляд является очень плохой идеей. И на мой взгляд это из области вот таких вот «кастомных решений»:

            +
            image


            1. caballero Автор
              28.08.2019 11:30

              Но всё равно надо признать что MVC это известный паттерн, а .Net MVC известная технология, которая легко понятна любому джуну и вполне себе имеет свои области применения.

              Ну страница с бекендом а-ля Razor Pages джунам будет как раз понятнее.

              Но вы вместо этого пытаетесь «перекрутить» MVC так, чтобы он стал похож на Razor Pages.

              Ну так в стандартном фрейморке Razor Pages нет. А для приведенного решения ничего переделывать не надо кроме конкретного контролера то есть можно так доделавыть или переделывать существующий проект на стандартном нет.
              Потому что вы протестировали вашу идею на плоской форме с двумя полями, но с такой формой и MVC справляется без проблем.

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

              Потому что новичкам нельзя будет дать мануал по MVC или Razor Pages, а надо будет объяснять как

              и не нужно. Это решение для тех кто уже в теме но хочет оптимизировать свой проект.
              Ну или представьте себе что вы по какой-то причине уйдёте из фирмы, а вашему преемнику придётся во всем этом разбираться без посторонней помощи…

              когда кто то уйдет и придется разбиратся — это будет не самая большая трудность. Обычно в серьезных проектах основная трудность — это бизгнес-логика, ообенно когда проекту не первый год, поменялось три команды а заказчик и сам уже не помнить зачем применялся тот или иной алгоритсмм.
              И вообще новые решения и технологии ща появляются каждый день — кто не умеет разбираться самостоятельно и осваивать новое тому в IT делать нечего.

              .


              1. Kanut
                28.08.2019 11:49

                Ну страница с бекендом а-ля Razor Pages джунам будет как раз понятнее.
                Если она местами будет выглядеть и вести себя как MVC, местами как Razor Pages, а местами как непонятно что? При отсутствии нормальных мануалов и помощи на каком-нибудь stackoverflow? Что-то я в этом сомневаюсь.

                Ну так в стандартном фрейморке Razor Pages нет.
                И вас этот факт вообще не смущает? Ведь если оно так просто, то что например мешало самому Microsoft добавить такую функциональность? :)
                И почему вообще сразу на Core не перейти?

                А на сложные страцы бывает и по несколько моделей городят.
                И как что-то подобное будет реализовано в вашем конструкте?

                Это решение для тех кто уже в теме но хочет оптимизировать свой проект.
                Это вообще пять баллов. То есть вы предлагаете уже имеющийся проект зачем-то «оптимизировать» и всё?
                А дальше его развивать уже не надо будет? Если нет, то зачем его тогда оптимизировать? А если надо, то кто это будет делать? Исключительно избранные сениоры, которые «в теме»?
                когда кто то уйдет и придется разбиратся — это будет не самая большая трудность.

                Сколько в вашем портфолио перенятых без посторонней поддержки легаси проектов? Сколько из них имели подобные «кастомизации»?

                И вообще новые решения и технологии ща появляются каждый день — кто не умеет разбираться самостоятельно и осваивать новое тому в IT делать нечего.
                Новые технологии обычно имеют хорошую документацию и комьюнити, с которой можно обсуждать баги/проблемы и их решения. Есть это у ваших «MVCRazorPages»? Или есть хотя бы шансы что это появится в обозримом будущем?


                1. caballero Автор
                  28.08.2019 16:33
                  -2

                  Если она местами будет выглядеть и вести себя как MVC, местами как Razor Pages, а местами как непонятно что?

                  чего там непонятного? как запрограмируют так и будет себя вести. Нет никаких технических проблем сочетать в одном проекте MVC, Razor Pages и например WEBAPI.

                  И как что-то подобное будет реализовано в вашем конструкте?

                  там нет никакого конструктора. Вы вообще читали статью?
                  Все что было моделями теперь будет в одном классе вместе с обработчиками как принято в парадигме ООП.

                  При отсутствии нормальных мануалов
                  там несколько строк как. Какие нужны мануалы? Этой статьи более чем достаточно. Все остальное в официальных доках.
                  Ведь если оно так просто, то что например мешало самому Microsoft добавить такую функциональность?

                  Потому что стандартный фреймворк развиватся уже не будет. И майкрософту наплевать будет ли оно просто. А вот сообществу работающему над Core.Net не наплевать. Потому и появились Razor PAges/

                  То есть вы предлагаете уже имеющийся проект зачем-то «оптимизировать» и всё?

                  Проекты которые на саппорте все равно доделываются и переделываются. Почему бы по дороге их не упростить.
                  Сколько в вашем портфолио перенятых без посторонней поддержки легаси проектов?

                  Хватает. Но оно не в моем портфолиро в компании где я работаю.
                  Новые технологии обычно имеют хорошую документацию и комьюнити
                  Это наверно в вашей параллельной вселенной так.

                  Есть это у ваших «MVCRazorPages»? Или есть хотя бы шансы что это появится в обозримом будущем?

                  Это простое решение в несколько срок на основе уже имеющейся платформы. Там неоткуда взяться багам и там нечего поддерживать Там даже на гитхаб нечего выкладывать отдельным проектом.
                  Вы или не поняли написанного или не читали вообще а просто возражаете на все коменты подряд.

                  Чтобы прекратить флуд — покажите хотя бы одну ТЕХНИЧЕСКИ обоснованую проблему которая возникнет изза предлагаемого решения. Аргумент типа — у кого то не хватит извилин разобраться в несколькизхс строках состоящих из стандартных функций описанных в документации майкрософта — это не аргумент. Большинство статей на хабре гораздо сложнее.



                  1. lair
                    28.08.2019 16:37
                    +1

                    покажите хотя бы одну ТЕХНИЧЕСКИ обоснованую проблему которая возникнет изза предлагаемого решения

                    Сначла покажите хотя бы одно технически обоснованное достоинство предлагаемого решения.


                  1. Kanut
                    28.08.2019 17:10

                    Как уже написали выше, мы в вашем решение пока не имеем ни одного технически обоснованного преимущества.

                    Но при этом покупаем кота в мешке что касается нормального функционирования кастомизации и её совместимости с различными сторонними решениями.
                    И как минимум получаем завышенный порог вхождения в проект для посторонних.

                    Лично для меня это уже причина оставить такую кастомизацию в стороне и либо взять .Net Core Razor Pages, либо посмотреть на другие имеющиеся альтернативы. И похоже я здесь не одинок в таком мнении. Но в конце-концов решение принимать вам.


    1. SemenPV
      27.08.2019 17:04

      Но это сразу же делает невозможным работу в двух окнах браузера!
      Чудес не бывает, можно конечно тянуть на клиет viewstate как в webforms, но если есть хороший session manager, то можно на клиенте для каждого открытого окна иметь только guid, а данные с сервера забирать.

      Проблема только с тем как правильно чистить старые данные.


      1. mayorovp
        27.08.2019 17:24

        Проще тогда уж использовать SPA фреймворки. Или серверный Blazor.


        1. SemenPV
          27.08.2019 17:42

          Если вы новый проект создаёте, то конечно всё что угодно можно взять. Но если у вас уже есть проект и требуется поддержка подобной функции, то уже ничего не поделаешь, придётся прикручивать.


          1. mayorovp
            27.08.2019 19:03
            +1

            Это вы о какой ситуации говорите? О ситуации вида "раньше сайт работал только строго в одной вкладке, а теперь надо разрешить пользователю открывать вторую"?


            В таком случае — да, без хитрого session manager не обойтись. Но я предлагаю в такую ситуацию просто не попадать.


  1. Ascar
    27.08.2019 19:41

    Слишком толсто.