ASP.NET MVC Урок 1-F / ASP.NET MVC Урок 5
.pdf$63 1(7 09& ǻȘȖȒ ǹȖȏȌȈȕȐȍ ȏȈȗȐșȐ Ȋ ǩǬtutorial
ASP*, .NET*
Цель урока. Отследить весь путь создания записи в БД и вывода его. Вывод ошибок. Валидация. Мапперы. Написание атрибута валидации. Капча. Создание данных в БД.
Введение
Наконец, переходим к одному из самых важных уроков, в котором будет рассказано про создание записей. Любое действие на сайте, от сложных, когда мы заполняем регистрационную анкету, до простых, когда ставим лайк, – происходит следующим образом:
Post\get запрос на сайт
Авторизация и аутентификация
Проверка введенных данных (валидация) на правильность
Если проверка введенных данных показала, что введенные данные неверны, то в заполняемую форму выводится предупреждение.
Если проверка введенных данных показала, что эти данные верны, то они сохраняются в БД и выводится страница с подтверждением.
Регистрация
Сделаем форму для регистрации пользователя. При регистрации, пользователь должен распознать капчу и повторить ввод пароля. Но начнем без этого. Создадим метод Register в
контроллере UserController и View.
SXEOLF $FWLRQ5HVXOW5HJLVWHU
^
YDUQHZ8VHU QHZ8VHU
UHWXUQ9LHZ QHZ8VHU
`
Создаем и передаем во View новый объект User. Так как полей у нас пока только два, для заполнения создаем View:
#XVLQJ +WPO %HJLQ)RUP 5HJLVWHU 8VHU )RUP0HWKRG 3RVW QHZ ^ #FODVV IRUP KRUL]RQWDO `
^
ILHOGVHW!
GLY FODVV FRQWURO JURXS!
ODEHO FODVV FRQWURO ODEHO IRU (PDLO!
(PDLO
ODEHO!
GLY FODVV FRQWUROV!
#+WPO 9DOLGDWLRQ0HVVDJH (PDLO
#+WPO 7H[W%R[ (PDLO 0RGHO (PDLO
GLY!
GLY!
GLY FODVV FRQWURO JURXS!
ODEHO FODVV FRQWURO ODEHO IRU )LUVW1DPH!
3DVVZRUG
ODEHO!
GLY FODVV FRQWUROV!
#+WPO 9DOLGDWLRQ0HVVDJH 3DVVZRUG
#+WPO 3DVVZRUG 3DVVZRUG 0RGHO 3DVVZRUG
GLY!
GLY!
GLY FODVV IRUP DFWLRQV!
EXWWRQ W\SH VXEPLW FODVV EWQ EWQ SULPDU\!
5HJLVWHU
EXWWRQ!
#+WPO$FWLRQ/LQN &DQFHO ,QGH[ QXOO QXOO QHZ ^ #FODVV EWQ `
GLY!
ILHOGVHW!
`
Все эти дивы, fieldset'ы и button’ы сделаны по подобию, как это описано в фреймворке bootstrap (далее будем изучать).
Изучим основные Htmlвставки:
+WPO %HJLQ)RUP5HJLVWHU 8VHU)RUP0HWKRG 3RVWQHZ^ #FODVV IRUP KRUL]RQWDO`
— формирует тегIRUP DFWLRQ ´ 8VHU 5HJLVWHU´ PHWKRG ´SRVW´ FODVV ´IRUP KRUL]RQWDO´! и
закрывает его после вызова 'LVSRVH(закрытие кавычек XVLQJ ^`)
#+WPO 7H[W%R[(PDLO0RGHO (PDLO
— формирует тег LQSXW W\SH ´WH[W´ QDPH ´(PDLO´ YDOXH ´#0RGHO (PDLO´!(т.е. в значение тега записывается значение Email переданного объекта)
#+WPO 9DOLGDWLRQ0HVVDJH3DVVZRUG
— выводит тег ошибки если такая есть
#+WPO 3DVVZRUG3DVVZRUG0RGHO 3DVVZRUG
— выводит тегLQSXW W\SH ´SDVVZRUG´ QDPH ´3DVVZRUG´ YDOXH ´#0RGHO 3DVVZRUG´!
После нажатия на кнопку Register идет Httpзапрос типа POST (так как)RUP0HWKRG 3RVW и
передает данные Email=&Password=.
Создадим метод Register, принимающий в качестве параметра тип User, и пометим его атрибутом HttpPost, а предыдущий — атрибутом HttpGet. Контроллер различает, какой из типов запроса сейчас происходит и перенаправляет на тот, который необходим:
>+WWS*HW@
SXEOLF $FWLRQ5HVXOW5HJLVWHU
^
YDUQHZ8VHU QHZ8VHU
UHWXUQ9LHZ QHZ8VHU
`
>+WWS3RVW@
SXEOLF $FWLRQ5HVXOW5HJLVWHU8VHU XVHU
^
UHWXUQ9LHZ XVHU
`
Сделаем точку останова на втором методе Register и проверим, какой объект приходит к нам:
Видим, что поля Email и Password заполнены, остальные остались нулевыми или по умолчанию
(default).
Так как мы должны принять еще 2 поля (повтор пароля и капчу), то добавим эти поля в наш User partial class:
SXEOLF SDUWLDO FODVV 8VHU
^
SXEOLF VWDWLF VWULQJ *HW$FWLYDWH8UO
^
UHWXUQ*XLG 1HZ*XLG 7R6WULQJ1
`
SXEOLF VWULQJ&RQILUP3DVVZRUG ^JHW VHW`
SXEOLF VWULQJ&DSWFKD ^JHW VHW`
`
Добавим поля во View:
GLY FODVV FRQWURO JURXS!
ODEHO FODVV FRQWURO ODEHO IRU )LUVW1DPH!
&RQILUP 3DVVZRUG
ODEHO!
GLY FODVV FRQWUROV!
#+WPO 9DOLGDWLRQ0HVVDJH &RQILUP3DVVZRUG
#+WPO 3DVVZRUG &RQILUP3DVVZRUG 0RGHO &RQILUP3DVVZRUG
GLY!
GLY!
GLY FODVV FRQWURO JURXS!
ODEHO FODVV FRQWURO ODEHO IRU )LUVW1DPH!
&DSWFKD
ODEHO!
GLY!
GLY FODVV FRQWURO JURXS!
ODEHO FODVV FRQWURO ODEHO IRU )LUVW1DPH!
Ɍɭɬ ɤɚɪɬɢɧɤɚ
ODEHO!
GLY FODVV FRQWUROV!
#+WPO 9DOLGDWLRQ0HVVDJH &DSWFKD
#+WPO 7H[W%R[ &DSWFKD 0RGHO &DSWFKD
GLY!
GLY!
Капчу пока не будем делать, просто она будет равна 1234.
Валидация
Условия для правильности данных:
Поле email не нулевое
Email – это корректно введенный адрес почты, т.е. с собачкой
Email добавляемый в БД — уникальный
Пароль не нулевой
Пароли совпадают Капча равна 1234
Если какоето из этих условий не соблюдается, то выдается ошибка.
IValidatableObject
Так как у нас класс User — partial, то мы можем реализовать для него IValidatableObject интерфейс, для этого, правда, придется добавить в проект System.Component.DataAnnotation. Это не очень хорошо, так как эта сборка необходима для валидации, а валидация – это прерогатива контроллеров в MVC. Так что мы тут немного нарушаем принцип.
Класс User:
SXEOLF SDUWLDO FODVV 8VHU ,9DOLGDWDEOH2EMHFW
^
SXEOLF VWDWLF VWULQJ *HW$FWLYDWH8UO
^
UHWXUQ*XLG 1HZ*XLG 7R6WULQJ1
`
SXEOLF VWULQJ&RQILUP3DVVZRUG ^JHW VHW`
SXEOLF VWULQJ&DSWFKD ^JHW VHW`
SXEOLF,(QXPHUDEOH 9DOLGDWLRQ5HVXOW!9DOLGDWH9DOLGDWLRQ&RQWH[W YDOLGDWLRQ&RQWH[W
^
ɇɟ ɧɭɥɟɜɨɣ (PDLO
LI VWULQJ,V1XOO2U:KLWH6SDFH (PDLO
^
\LHOGUHWXUQ QHZ9DOLGDWLRQ5HVXOW ȼɜɟɞɢɬɟ HPDLOQHZVWULQJ>@ ^(PDLO`
`
ɤɨɪɪɟɤɬɧɵɣ (PDLO
YDUUHJH[ QHZ5HJH[# ?Z > @?Z #?Z > @?Z ? ?Z > @?Z5HJH[2SWLRQV & RPSLOHG
YDUPDWFK UHJH[ 0DWFK (PDLO
LIPDWFK 6XFFHVV PDWFK /HQJWK (PDLO /HQJWK
^
\LHOGUHWXUQ QHZ9DOLGDWLRQ5HVXOW ȼɜɟɞɢɬɟ ɤɨɪɪɟɤɬɧɵɣ HPDLOQHZVWULQJ>@ ^(PD LO`
`
ɩɚɪɨɥɶ ɧɟ ɧɭɥɟɜɨɣ
LI VWULQJ,V1XOO2U:KLWH6SDFH 3DVVZRUG
^
\LHOGUHWXUQ QHZ9DOLGDWLRQ5HVXOW ȼɜɟɞɢɬɟ ɩɚɪɨɥɶQHZVWULQJ>@ ^3DVVZRUG`
`
ɩɚɪɨɥɢ ɫɨɜɩɚɞɚɸɬ
LI3DVVZRUG &RQILUP3DVVZRUG
^
\LHOGUHWXUQ QHZ9DOLGDWLRQ5HVXOW ɉɚɪɨɥɢ ɧɟ ɫɨɜɩɚɞɚɸɬQHZVWULQJ>@ ^&RQILUP3
DVVZRUG`
`
`
`
Мы смогли сделать проверку 4 из 6 правил валидации, но оставим пока так, а остальные добавим непосредственно в контроллере.
Выполняем форму, получаем:
Видим, что обе наши ошибки были отловлены.
Есть два стандартных метода вывести ошибку: это Html.ValidationMessage(“ErrorField”) и Html.ValidationSummary(). Первый выводит ошибку, связанную с конкретным неверновведенным полем, а второе — выведет все (или все оставшиеся) ошибки.
Добавляем в контроллер проверку на капчу и проверку на существование Email в БД
(/Areas/Default/UserController.cs:Register):
LIXVHU &DSWFKD
^
0RGHO6WDWH$GG0RGHO(UURU&DSWFKD Ɍɟɤɫɬ ɫ ɤɚɪɬɢɧɤɢ ɜɜɟɞɟɧ ɧɟɜɟɪɧɨ
`
YDUDQ\8VHU 5HSRVLWRU\ 8VHUV$Q\ S !VWULQJ&RPSDUH S (PDLO XVHU (PDLO
LIDQ\8VHU
^
0RGHO6WDWH$GG0RGHO(UURU(PDLO ɉɨɥɶɡɨɜɚɬɟɥɶ ɫ ɬɚɤɢɦ HPDLO ɭɠɟ ɡɚɪɟɝɢɫɬɪɢɪɨɜɚɧ
`
И результат:
Что ж, с задачей мы справились, но в дальнейшем, используя такой способ, мы получим несколько проблем:
Класс User всегда будет содержать проверку на необходимость введения пароля и идентичность паролей, а, например, при изменении данных в личном кабинете, мы вообще не должны вводить пароль. Т.е. необходимо будет вводить другие поля, которые будут обозначать: это регистрация, это смена пароля, это изменение данных.
Валидацию мы сделали частично в Modelчасти и частично в Controllerчасти – это не совсем хрестоматийно.
Но есть решение, мы создаем класс, который является представлением класса User, организующим валидацию. Мы назовем его UserView и создадим в папке Models/ViewModels:
SXEOLF FODVV 8VHU9LHZ
^
SXEOLF LQW,' ^JHW VHW`
SXEOLF VWULQJ(PDLO ^JHW VHW`
SXEOLF VWULQJ3DVVZRUG ^JHW VHW`
SXEOLF VWULQJ&RQILUP3DVVZRUG ^JHW VHW`
SXEOLF VWULQJ&DSWFKD ^JHW VHW`
SXEOLF VWULQJ $YDWDU3DWK ^JHW VHW`
`
Automapping
Прежде чем приступить к использованию этого класса, стоит заметить, что это не совсем удобно. Мы создали совершенно другой класс, но добавлять в БД мы должны класс User, а это означает, что в какомто месте программы мы должны передавать от объекта UserView в User поля, так и наоборот. А при большом количестве объектов и полей – это рутинно, к тому же, подобное у нас уже есть в функции Update[Table] в репозитории. Для решения этой задачи существуют так называемые мапперы objecttoobject.
Одним из самых популярных, является automapper (http://automapper.org/). Собственно, эта библиотека берет на себя работу по переводу одного объекта в другой, и, как мы дальше увидим, там еще есть много других вкусных плюшек.
Устанавливаем Automapper:
,QVWDOO 3DFNDJH$XWR0DSSHU
Так как при разработке программы мы избегаем сильную связность, то организуем интерфейс + реализацию и зарегистрируем это в Ninject, после чего выведем использование в контроллер.
Создаем в /Mappers:
SXEOLF LQWHUIDFH ,0DSSHU
^
REMHFW 0DS REMHFWVRXUFH 7\SH VRXUFH7\SH 7\SH GHVWLQDWLRQ7\SH
`
Реализация:
SXEOLF FODVV &RPPRQ0DSSHU ,0DSSHU
^
VWDWLF &RPPRQ0DSSHU
^
0DSSHU &UHDWH0DS 8VHU 8VHU9LHZ!
0DSSHU &UHDWH0DS 8VHU9LHZ 8VHU!
`
SXEOLF REMHFW 0DS REMHFWVRXUFH 7\SH VRXUFH7\SH 7\SH GHVWLQDWLRQ7\SH
^
UHWXUQ0DSSHU 0DS VRXUFH VRXUFH7\SH GHVWLQDWLRQ7\SH
`
`
Регистрация (пусть будет как объектодиночка) (/App_Start/NinjectWebCommon.cs):
NHUQHO %LQG ,0DSSHU! 7R &RPPRQ0DSSHU! ,Q6LQJOHWRQ6FRSH
В BaseController (/Controllers/BaseController.cs):
SXEOLF DEVWUDFW FODVV %DVH&RQWUROOHU &RQWUROOHU
^
>,QMHFW@
SXEOLF,5HSRVLWRU\ 5HSRVLWRU\ ^JHW VHW`
>,QMHFW@
SXEOLF,0DSSHU 0RGHO0DSSHU ^JHW VHW`
`
Теперь изменим UserController (и View) с использованием UserView:
>+WWS*HW@
SXEOLF $FWLRQ5HVXOW5HJLVWHU
^
YDUQHZ8VHU9LHZ QHZ8VHU9LHZ
UHWXUQ9LHZ QHZ8VHU9LHZ
`
>+WWS3RVW@
SXEOLF $FWLRQ5HVXOW5HJLVWHU8VHU9LHZ XVHU9LHZ
^
LIXVHU9LHZ &DSWFKD
^
0RGHO6WDWH$GG0RGHO(UURU&DSWFKD Ɍɟɤɫɬ ɫ ɤɚɪɬɢɧɤɢ ɜɜɟɞɟɧ ɧɟɜɟɪɧɨ
`
YDUDQ\8VHU 5HSRVLWRU\ 8VHUV$Q\ S !VWULQJ&RPSDUH S (PDLO XVHU9LHZ (PDLO
LIDQ\8VHU
^
0RGHO6WDWH$GG0RGHO(UURU(PDLO ɉɨɥɶɡɨɜɚɬɟɥɶ ɫ ɬɚɤɢɦ HPDLO ɭɠɟ ɡɚɪɟɝɢɫɬɪɢɪɨɜɚ ɧ
`
LI0RGHO6WDWH ,V9DOLG
^
YDUXVHU 8VHU 0RGHO0DSSHU 0DS XVHU9LHZW\SHRI8VHU9LHZW\SHRI8VHU
72'2 ɋɨɯɪɚɧɢɬɶ
`
UHWXUQ9LHZ XVHU9LHZ
`
И в Register.cshtml изменится первая строка:
#PRGHO /HVVRQ3URMHFW 0RGHOV 9LHZ0RGHOV 8VHU9LHZ
Атрибуты
Для UserView будем использовать для валидации атрибуты.
Добавим сборку:
XVLQJ6\VWHP &RPSRQHQW0RGHO 'DWD$QQRWDWLRQV SXEOLF FODVV 8VHU9LHZ
^
SXEOLF LQW,' ^JHW VHW`
>5HTXLUHG (UURU0HVVDJH ȼɜɟɞɢɬɟ HPDLO@
SXEOLF VWULQJ(PDLO ^JHW VHW`
>5HTXLUHG (UURU0HVVDJH ȼɜɟɞɢɬɟ ɩɚɪɨɥɶ@
SXEOLF VWULQJ3DVVZRUG ^JHW VHW`
>&RPSDUH3DVVZRUG(UURU0HVVDJH ɉɚɪɨɥɢ ɞɨɥɠɧɵ ɫɨɜɩɚɞɚɬɶ@
SXEOLF VWULQJ&RQILUP3DVVZRUG ^JHW VHW`
SXEOLF VWULQJ&DSWFKD ^JHW VHW`
SXEOLF VWULQJ $YDWDU3DWK ^JHW VHW`
`
Проверяем: