веб приложение на react с базой данных
Начало работы с Postgres в вашем приложении React
Разработчики внешнего интерфейса обычно не должны понимать процесс получения, записи и удаления информации. Это работа для разработчиков бэкэнда.
Тем не менее, есть много веских причин для того, чтобы веб-разработчик узнал о бэкэнд-программировании и взаимодействии с базой данных. Например:
В этом руководстве мы покажем, как создать небольшое приложение с использованием Express и Node.js, которое может записывать и удалять информацию из базы данных PostgreSQL в соответствии с полученными запросами HTTP. Затем мы создадим простое приложение React, чтобы протестировать и посмотреть, как все приложение движется спереди назад.
Я предполагаю, что вы понимаете, как работает приложение React, и знакомы с внешними HTTP-запросами JavaScript. Мы не будем рассказывать о том, как проверять данные, прежде чем взаимодействовать с базой данных. Вместо этого мы сосредоточимся на том, чтобы показать, как запросы от интерфейса записываются в базу данных.
Я опубликовал репозиторий GitHub для этого урока, чтобы вы могли сравнить свой код, если застряли. Теперь давайте запустим нашу базу данных.
Настройка PostgreSQL
После открытия pgAdmin запросит ваш пароль для входа в систему. Ниже приведен обзор недавно установленной базы данных PostgreSQL.
Создание базы данных Postgres
Чтобы лучше понять язык SQL, нам нужно создать базу данных и таблицу из терминала.
Вам будет предложено ввести пароль. Используйте пароль, который вы создали ранее. После входа в систему создайте нового пользователя, добавив разрешение на вход с паролем «root».
База данных на основе SQL хранит данные внутри таблицы. Теперь, когда у вас есть база данных, давайте создадим простую таблицу, в которую вы сможете записывать свои данные.
Теперь есть таблица, в которую вы можете вставить данные. Давайте сделаем это дальше.
Основные SQL-запросы
1. Выберите запрос
2. Вставьте запрос
3. Удалить запрос
4. Обновить запрос
Теперь, когда вы знаете, как манипулировать данными внутри таблицы, давайте рассмотрим, как подключить вашу базу данных к React.
Создание сервера API с Node.js и Express
Чтобы связать ваше приложение React с базой данных PostgreSQL, вы должны сначала создать сервер API, который может обрабатывать HTTP-запросы. Давайте настроим простой, используя NodeJS и Express.
Создайте новый каталог и установите новый пакет npm из вашего терминала с помощью следующих команд.
Вы можете заполнить информацию о вашем пакете, как вам нравится, но вот пример моего package.json :
Далее установите необходимые пакеты.
После установки обоих index.js файлов создайте файл со следующим содержимым.
Теперь у вас есть все, что вам нужно, чтобы написать свой API.
Заставить NodeJS поговорить с Postgres
Библиотека pg позволяет приложению Node общаться с Postgres, поэтому вы захотите сначала импортировать его. Создайте новый файл с именем merchant_model.js и введите следующий код.
Обратите внимание, что ввод учетных данных, таких как пользователь, хост, база данных, пароль и порт, как в примере выше, не рекомендуется в производственной среде. Мы будем хранить его в этом файле, чтобы упростить учебник.
Создание вашего приложения React
Ваш API готов обслуживать и обрабатывать запросы. Теперь пришло время создать приложение React для отправки запросов в него.
Теперь давайте напишем простое приложение React с нуля.
Сначала создайте файл App.js со следующим содержимым.
createMerchant и deleteMerchant начнется процесс добавления и удаления продавцов, соответственно, при нажатии на кнопки.
Вывод
Теперь вы знаете, как установить базу данных PostgreSQL, создать базу данных и таблицу, а также создать минимальный API, который будет служить мостом между вашим приложением React и вашей базой данных. Мы создали полный пример того, как использовать Postgres с React, и продемонстрировали, что именно происходит, когда вы отправляете эти HTTP-запросы из приложения React.
Это руководство далеко не полное руководство по программированию бэкэнда, но этого достаточно, чтобы помочь вам начать понимать, как работает бэкэнд.
Полная видимость в производственных приложениях React
Отладка приложений React может быть сложной, особенно когда пользователи сталкиваются с проблемами, которые трудно воспроизвести. Если вы заинтересованы в мониторинге и отслеживании состояния Redux, автоматическом обнаружении ошибок JavaScript и отслеживании медленных сетевых запросов и времени загрузки компонентов, попробуйте LogRocket.
LogRocket похож на видеорегистратор для веб-приложений, записывая буквально все, что происходит в вашем приложении React. Вместо того, чтобы гадать, почему возникают проблемы, вы можете агрегировать и сообщать, в каком состоянии было ваше приложение, когда возникла проблема. LogRocket также отслеживает производительность вашего приложения, предоставляя отчеты с такими показателями, как загрузка ЦП клиента, использование памяти клиента и т. д.
Промежуточный пакет LogRocket Redux добавляет дополнительный уровень видимости в ваши пользовательские сессии. LogRocket записывает все действия и состояние из ваших магазинов Redux.
Пишем полноценное приложение на React с нуля за час
Авторизуйтесь
Пишем полноценное приложение на React с нуля за час
В этой статье вы познакомитесь с React — библиотекой для создания пользовательских интерфейсов. React появился в 2013 году и достаточно быстро стал популярным среди разработчиков. Сегодня в работе над веб-приложениями его используют Facebook, Instagram, Trello, AirBnb, PayPal. С помощью этой статьи мы сможем написать приложение прогноза погоды: от установки с помощью create-react-app (проект на GitHub) до подключения API и стилей bootswatch.
Прим. перев. Если вы начинаете изучение React, вам также стоит прочитать наш материал, в котором разработчик делится советами по использованию этой библиотеки.
Этот материал был написан для воркшопа Open Source Dev Garage, прошедшего в рамках конференции разработчиков F8 2017. Чтобы лучше разобраться в том, как написать это приложение, посмотрите 48-минутное видео или следуйте письменным инструкциям в этом руководстве.
Просмотрев семинар или изучив руководство, вы создадите простое приложение прогноза погоды:
Создайте ваше первое приложение
Прежде всего вам понадобится node.js и редактор кода, например, Atom.
Откроем терминал и установим create-react-app :
Начнем создавать наше приложение прогноза погоды:
Данной командой мы установим набор инструментов, которые помогут создать наше React-приложение. По завершении установки мы сможем запустить приложение командами:
Новое приложение автоматически откроется в браузере!
Свойства и компоненты
Давайте взглянем на приложение, которое create-react-app создал автоматически. В редакторе кода откроем weather/src/App.js :
Наше приложение состоит из одного компонента, где функция render() является его главной составляющей. Напишите какой-нибудь текст, сохраните изменения и посмотрите, как приложение автоматически применит их!
Где-нибудь в начале файла добавим несколько городов, для которых мы хотели бы отобразить погоду:
Из массива данных мы создадим набор элементов button и назначим свойство key для каждого, чтобы React знал последовательность элементов в массиве.
На этом этапе файл App.js должен выглядеть так.
Состояние
Применим this.state и this.setState в нашем компоненте App :
На этом этапе файл App.js должен выглядеть так.
Жизненный цикл компонентов и выборка данных
Иногда нам нужно добавить императивный код (React-код обычно декларативен), который вызывается в определенное время жизни компонента. Методы жизненного цикла позволяют нам написать дополнительный код как раз для таких случаев.
Улучшим вывод render() для красивого вывода данных:
На этом этапе файл App.js должен выглядеть так.
Установка компонентов
Импортируем стили из bootstrap в начале файла:
Далее импортируем из react-bootstrap компоненты, которые мы хотим использовать. Их можно найти на сайте react-bootstrap:
Теперь наше приложение выглядит привлекательнее, но будет здорово, если мы добавим что-то от себя. Для этого установим bootswatch :
Окончательный вид нашего приложения:
Развертывание (дополнительный материал)
Прежде всего опубликуйте ваш код на Github, затем перейдите в ваш репозиторий и откройте файл ReadMe, в котором вы найдете инструкцию по развертыванию приложения на различных популярных сервисах.
Одним из таких сервисов является Netlify, особенно в случае, когда вы хотите использовать механизм «непрерывного развертывания».
Offline-first приложение с Hoodie & React. Часть первая: браузерная база данных
Современный веб позволяет решать часть задач которые раньше были прерогативой нативных мобильных приложений. Мы с вами создадим веб-приложение (сайт) которое будет загружаться и сохранит полную функциональность даже в отсутствии интернета, а при его появлении автоматически синхронизируется с сервером. На мобильном устройстве для такого приложения достаточно создать ярлык и в плане автономности мы получим аналог нативного приложения.
Мы напишем подобие todo-листа, с одним отличием: «выполненные» задачи будут не удаляться, а переноситься в конец списка и по мере решения остальных задач всплывать вверх. Такой список удобно использовать для повторяющихся вещей, таких как различные спортивные активности, развлечения, еда и т.п. Одна моя социально-реализованная знакомая использует его, чтобы равномерно поддерживать контакт с многочисленной популяцией своей френдзоны.
То, что получится в результате можно посмотреть тут. Попробуйте внести некоторые изменения, закрыть вкладку, отключить интернет и снова открыть сайт. Вы обнаружите, что он открывается и сохраняет полную функциональность. Если вы залогинитесь на разных устройствах и внесёте изменения в оффлайне, по восстановлении соединения изменения синхронизируются интуитивно ожидаемым образом.
Вы удивитесь насколько мало кода нам потребуется для реализации этого функционала.
Часть 0: настройка окружения
Попробуйте запустить сервер. Когда вебпак скажет, что всё готово, по адресу http://localhost:8000/ вы должны увидеть симпатичную шапку сайта.
Часть 1: браузерная бд
Для функционирования приложения в оффлайн-режиме, нам потребуется где-то хранить данные и как-то синхронизировать их между устройствами. Pouchdb подходит для этого идеально. Но мы и с ней будем работать не напрямую а через обёртку Hoodie, которая умеет авторизацию. Давайте подключим её в src/App.js :
Добавление документа в базу
Теперь создадим компоненту для добавления loop-а (отдельного todo-листа):
Чтение базы данных
Давайте теперь отобразим сохранённые в базу документы. В App.js мы добавим метод loadLoops :
Давайте теперь отрисуем их, добавив в render() :
Теперь вы должны видеть список добавленных loop-ов. Однако при добавлении новых они появятся в списке только при перезагрузке страницы. Давайте исправим это.
Подписка на изменения бд
Весь код этого пункта будет состоять из добавления пары строчек в наш App.js :
Теперь при добавлении loop-ов они будут автоматически появляться в списке. Причём независимо от того, каким образом изменилась база данных. Это важно — даже если изменения произошли в ходе синхронизации с другим устройством, наш коллбэк сработает.
Учимся быть фуллстек разработчиками. Пишем приложение на React/Redux/Webpack/ASP.NET Core 2.0/EF Core
После этого туториала, я надеюсь, веб и бэк разработчикам будет проще найти почву под ногами в вражьей области и понять в какую сторону двигаться для более углубленного изучения. Поехали!
Итак, нам понадобятся
2. Node.js с установленным Node Package Manager (NPM). Node.js в нашем проекте нам нужна только для 2 мастхэв тулзов веб.разработки – это Webpack(для сборки и обработки различными прелоадерами нашего клиентского кода) и NPM(для установки js утилит/компонентов/пакетов)
Что будем писать?
Разбираться будем на примере разработки очень простого блога, который умеет выводить список постов на главной странице, переходить на отдельную страницу с комментариями (с возможностью комментировать), выполнять авторизацию его владельца и давать ему возможность написать новый пост.
Часть 1. Бэкенд
Бэк из себя будет представлять набор restful api для клиента, базу будем использовать ms sql. Для работы с базой – EntityFramework Core, Code First подход.
Создаем пустой ASP.NET Core Web Application проект, будем дописывать в дальнейшем все необходимое руками.
В новом проекте, в классе Startup (файл Startup.cs), который является основным конфигурационным файлом в asp.net core, подключим сервис и middleware МVС (подробнее про цепочку middleware и обработку запроса пользователя с помощью слоев middleware, можно почитать в документации microsoft). Как известно в asp.net core весь статический контент(js/css/img) должен лежать в папке wwwroot (по умолчанию, если не указана другая папка), для того чтобы этот контент отдать конечному пользователю мы должны прописать еще один слой middleware – вызывать extension метод UseStaticFiles. В итоге у нас получится класс Startup с следующим кодом:
Теперь займемся бизнес логикой. Создадим в нашем solution два новых проекта Class Library (.NET Standart):
Таким образом мы изолируем репозиторий, приложение и модель друг от друга. И в дальнейшем, например, можем заменить сборку работы с базой DBRepository на другую сборку, которая работает с базой не через EF Core, а через какую либо другую ORM или через ado.net. Или, например, сможем подключить сборку репозитория и модели, не к веб приложению, а к десктоп.
Далее, добавим в проект Models классы, которые будут в дальнейшем мапиться на таблицы в базе, останавливаться на этом подробно я не буду, на диаграмме классов, по-моему, все говорит само за себя:
Теперь перейдем в проект DBRepository и установим два nuget пакета, которые нам понадобяться для работы с EF Core — Microsoft.EntityFrameworkCore и провайдер бд MS SQL Server Microsoft.EntityFrameworkCore.SqlServer
Создадим наследника от класса DBContext, основого класса EF — точки входа для работы с данными. И фабрику (интерфейс+реализация), которая будет создавать этот контекст. Для чего нам понадобится фабрика мы разберемся позже.
Код очень простой – в фабрике мы конфигурируем dbcontext для работы с SQL Server и передаем строку подключения к бд(без connection string никуда…). Extension метод UseSqlServer к нам пришел из пакета Microsoft.EntityFrameworkCore.SqlServer.
Воспользуемся популярным паттерном «Репозиторий» и создадим классы-посредники, которые будут «отгораживать» наши конечные классы-потребители от работы с базой и EntityFramework в частности.
Добавим класс BaseRepository, интерфейс IBlogRepository и его реализацию BlogRepository.
BaseRepository будет являться, как видно из названия, базовым классом для всех созданных нами классов-посредников. В конструктор он принимает строку подключения и фабрику для создания EF контекста.
В классе BlogRepository, не откладывая в долгий ящик, мы уже реализовали метод для получения списка постов с постраничным выводом.
В строке 1 мы наконец то создаем контекст для работы с БД. В строке 2, используя методы LINQ, которые EF транслирует в sql скрипты, мы пишем запрос для получения нужной нам страницы постов вместе с тегами (с помощью метода Include). Важно помнить, что LINQ методы выполняются lazy, и поэтому само обращение к базе будет только после вызова метода ToListAsync в строке 3 (и CountAsync). После того как данные были получены, контекст работы с бд необходимо закрыть (обернуть его создание в using, как сделано в данном случае).
Вернемся в проект с веб-приложением и добавим конфигурационный файл. Созданный файл уже будет содержать поле с строкой подключения, нам нужно лишь отредактировать ее и указать актуальную базу и сервер.
В строках 1 и 2 регистрируем реализации с помощью метода AddScope.
Вообще существуют 3 метода регистрации реализации – AddScope, AddTransient, AddSingleton, они различаются лишь временем жизни регистрируемого инстанса.
AddScope – инстанс создается 1 раз на каждый request от клиента к серверу.
AddTransient — каждый раз при резолве зависимости создается новый инстанс
AddSingleton – инстанс создается в единственном экземпляре и не меняется между запросами.
Подробнее можно прочитать в документации.
Теперь создадим контроллер с API методом и уже можно что-то щупать руками.
Зависимость IBlogRepository автоматически зарезолвится IoC контейнером.
Нажимаем F5, забиваем в адресную строку браузера урл к нашему api методу — localhost:64422/api/blog/page и… получаем следующий эксепшен, если конечно мы верно изменили строку подключения:
Дело в том, что нашей базы еще нет, и естественно EF не может открыть несуществующую базу. Нужно каким-то образом сообщить, что в случае если бд нет, EF должен ее создавать.
Можно просто вызывать метод context.Database.EnsureCreated перед обращением к базе, но чаще используют механизм миграций. Он позволяет, в случае если у нас была изменена схема данных, аккуратно применить ее на базу без потери существующих данных. Давайте попробуем.
Для работы утилиты миграции необходимо, чтобы наследник класса DBContext (в нашем случае класс RepositoryContext) был доступен из вне, и утилита миграций при запуске смогла его «вытащить» и использовать для своих нужд. Для этого мы должны:
либо зарегистрировать RepositoryContext в сервисах, но мы не очень хотим завязываться в нашем проекте на контекст, не зря же мы создавали классы-посредники к репозиториям
либо реализовать следующий интерфейс
Выберем последний вариант.
Пускай вас не смущает еще одна фабрика для создания контекста, она нужна ТОЛЬКО для утилиты миграции.
Итак, реализуем интерфейс:
Реализация очень проста — вытаскиваем строку подключения из конфига, создаем и возвращаем DBContext.
Теперь открываем Powershell консоль, выбираем проект DBRepository и добавляем миграцию.
Вуаля, миграция создалась, в проект DBRepository добавилась папка Migrations, содержащая новые файлы с автоматически сгенерированными классами и методами Up для инкремента миграции, Down для декремента.
Предыдущей командой мы лишь создали классы в нашем проекте, теперь необходимо применить миграцию на базу.
Давайте пропишем в коде автоматическое применение к базе, чтобы каждый раз, при очередной миграции, вручную не вызывать команду Update-Database и чтобы у тех кто в дальнейшем будет работать с нашим кодом применились все миграции и создалась база.
Перейдем в файл Program.cs и напишем следующие заветные строки.
В строке 1 мы создаем конфиг, в строке 2 создаем новый scope, чтобы получить экзмпляр RepositoryContextFactory (мы же помним, что зарегистрировали его с временем жизни scope? Без scope не будет и экземпляра), в строке 3 Метод DBContext.Database.Migrate() накатывает на базу все миграции, которых еще нет в базе. И если это первый вызов, когда базы еще нет, то создает ее. Далее диспозим скоуп, т.к. нам он больше не нужен.
А как механизм миграции узнает, какие миграции уже есть в базе, а каких еще нет? Все очень просто. При выполнении создается таблица __EFMigrationsHistory, где записаны имена всех миграций которые были применены.
С бэком, на данном этапе, мы практически разобрались, можно перейти к фронту, но перед нами возникает дилемма – разрабатывать фронт в отдельном проекте и в дальнейшем хостить раздельно или оставить его в том же проекте, что и web api, что конечно облегчит разработку и деплой. Также уже есть готовые темплейты в Visual Studio, которые рутинные вещи такие как роутинг и сборку webpack превращают в middleware магию, с помощью nuget-пакета Microsoft.AspNetCore.SpaServices.
Первый вариант вариант безусловно гибче, он позволяет отдельно деплоить веб и бэк, разрабатывать их в разных средах, но второй вариант тоже вполне имеет право на существование, в нашем случае, все же перевешивает простота и наглядность, поэтому, сделаем над собой усилие, и мужественно пойдем по второму, более легкому пути.
Создадим вьюху-контейнер клиентского приложения (Views/Home/Index.cshtml), контроллер, что будет ее отдавать и пропишем роутинг.
Часть 2. Фронтенд
Пришло время удивительных историй приступить к визуальной части нашего приложения. Node.js с менеджером пакетов (NPM) у нас уже должна стоять, если нет, то необходимо поставить (https://nodejs.org).
Тут надо оговориться, что в Visual Studio есть темплейт по созданию SPA приложения в связке с asp.net core, но мы будем руководствоваться принципом, если хочешь в чем-то разобраться, сделай это своими руками. К тому же, на мой взгляд, тот темплейт несколько избыточен и многое там придется удалять.
Далее вам необходимо скачать ряд npm пакетов, которые must have для нашей дальнейшей разработки. Можно ставить каждый пакет в отдельности выполняя команду npm i
[—save-dev], или сразу прописать все зависимости в нашем проектном файле и выполнить npm install. Для экономии времени и места в статье приведу package.json с всеми зависимостями.
В этом файле, помимо стандартно сгенерированных полей, мы видим 2 секции – devDependencies и dependencies, это список пакетов которые мы ставим. Отличие одной секции от другой состоит в том, что в секции devDependencies мы прописываем пакеты, которые необходимы ТОЛЬКО для сборки нашего приложения – различные лоадеры, прелоадеры, преобразователи, упаковщики, а в секции dependencies – пакеты необходимые именно для разработки, которые мы будем импортировать и использовать для написания кода.
Кратко пробежимся по пакетам:
Ну и Visual Studio должна увидеть эти пакеты и отобразить их у себя под вкладкой Dependencies
Далее перейдем в файл Startup.cs и пропишем middleware магию, о которой говорилось выше. Для этого нам необходимо поставить пакет Microsoft.AspNetCore.SpaServices, ну или он уже есть в составе пакета Microsoft.AspNetCore.All.
В строке 1 мы включаем поддержку webpack, теперь у нас не будет болеть голова за сборку клиентских ресурсов и нам не нужно будет каждый раз вручную ее запускать, прописывать куда нибудь в PostBuildEvents или держать открытым окошко консоли с запущенной webpack –watch. Этой строкой мы создаем инстанс webpack-а в памяти, который будет отслеживать изменения в файлах и запускать инкрементальную сборку.
В строке 2 мы включаем поддержку клиентского роутинга. Попробую вкратце описать проблему, которую решает данная строка. Как вы знаете философия SPA такова, что пользователю в браузер всегда загружена одна станица и новые данные подгружаются ajax запросами. И весь роутинг, который меняется в строке запроса браузера, это на самом деле не ресурсы сервера, а клиентская «эмуляция запросов» к данным ресурсам. Пользователь все равно остается на одной и той же странице. Проблем нет, если пользователь последовательно переходит к нужному ресурсу, но, если он решит сразу перейти к этому ресурсу напрямую, тогда проблемы появятся.
Например, ваше приложение располагается по адресу www.mytestsite.com. Пользователь нашел интересный контент на странице www.mytestsite.com/home/news?id=1 и решил скинуть ссылку другу или подруге. Друг/подруга получает эту ссылку, копирует в браузер, и получает 404 ошибку. Проблема тут в том, что на сервере физически нет этого ресурса и веб-сервер понятия не имеет как роутить данный url, потому что у нас SPA приложение и потому что был организован клиентский роутинг.
Так вот строка 2 в Startup.cs на все подобные запросы отдает страницу контейнер, та приезжает клиенту и уже на клиенте приложение разруливает роутинг.
Теперь нам нужно создать файл-конфиг для webpack(webpack.config.js), руководствуясь которым, webpack будет применить различные препроцессоры и собирать наше приложение. Создадим его там же где у нас лежит package.json.
Кратко пробежимся по конфигу. За подробной документацией лучше обратиться к официальным источникам. Также есть отличный скринкаст по вебпаку, к сожалению, он только по webpack 1.x, но основные вещи и концепции вполне можно посмотреть и там.
Итак, первым делом нам нужно указать точку входа webpack в наши исходники — значение поля entry, это и есть она родимая.
Далее в output указываем куда webpack должен положить результат своей работы (bundle).
В devtool указываем, что нужно создать source-map, чтобы при отладке не лазить по огромному бандлу, а была привязка к исходникам.
И наконец в секции module указываем какие лоадеры нужно подключить. Пока подключим только модуль babel и пресеты для него – react для трансформации jsx синтаксиса, es2015 для поддержки ES6, stage-0 для использования новых js фич
Ну что же, мы закончили с настройкой, и наконец то готовы приступить к самой разработке.
Давайте создадим директорию App в корне нашего asp.net core приложения, и в ней добавим файл index.jsx. Приведу ниже скрин из solution explorer, чтобы вы не заплутали.
В index.jsx напишем следующее:
Это будет входной точкой в наше клиентское приложение. Вкратце, в 2 верхних строчках мы импортируем все для разработки на React, в 3-й строке мы импортируем наш компонент-контейнер App, который напишем ниже. В 4-й строке рендерим этот компонент в DOM элемент с
Создадим файл app.jsx, про который я упоминал выше:
Что тут интересного. Во-первых, мы объявили новый компонент строкой export default class App extends React.Component и экспортировали его, чтобы он был доступен извне.
Во-вторых, мы импортировали набор компонентов из пакета react-router-dom для организации клиентского роутинга. Router – это root компонент роутинга, в который должны быть вложены все остальные, а Switch и Route это сама организация роутинга. В данном случае мы хотим, чтобы при обращении к корневому пути (для примера — www.mytestsite.com) выводился наш компонент Blog, а при обращении к пути /about (www.mytestsite.com/about) выводился компонент About.
Header, About, Blog это наши пользовательские компоненты, давайте быстро накидаем их фейки. Создайте 3 директории в том же каталоге, что и app.jsx, в новых каталогах создайте по файлу и скопипастите следующий код, изменив имя класса, ну и текст в div
Теперь перейдем в новосозданный header.jsx и сделаем навигацию.
Тут ничего нового, за исключением компонента Link. Компонент будет генерировать ссылку, которая не будет отсылать вас на сервер, а будет изменять строку запроса в браузере, добавлять запись в историю, в общем вести себя как обычная ссылка, но без перезагрузки страницы.
Теперь давайте запустим проект и посмотрим, что у нас получилось. Не забудем подключить итоговый бандл, результат работы webpack, на нашу страницу-контейнер Index.cshtml
Конечно визуальной красоты пока мало, но css, стили, картинки и прочее наведение марафета, не входит в рамки этой статьи.
Пощелкаем на ссылки и убедимся, что страница не перезагружается, а строка запросов в браузере изменяет свой url. Работает? Поехали дальше.
Теперь пришло время подумать над архитектурой нашего клиентского приложения, да и в целом над организацией расположения файлов, компонентов и т.д. На эту тему конечно сломано много копий, прошло много холиваров, и один из подходов, что файлы удобно группировать вокруг функций(фич). Давайте придерживаться этого правила и в нашем фронтенд проекте.
Что касается архитектуры мы будем использовать популярный сейчас Redux. Когда мы настраивали webpack мы уже установили все необходимые пакеты для него (redux, redux-thunk, react-redux). Подробнее про Redux лучше почитать в документации. Есть ее полный перевод на русский.
Итак, давайте займемся нашим блогом, а именно выводом ленты сообщений.
Тут надо сказать несколько слов о архитектуре Redux. Вся архитектура построена вокруг ключевых понятий – action/reducer/store/view. Основной идей этой архитектуры, является то, что состояние нашего приложения хранится в одном месте(store) и влиять на это состояние могут лишь так называемые чистые функции(reducers), которые просто берут предыдущее состояние, по флагу(ключу) определяют, как нужно его изменить и возвращают новое состояние.
В свою очередь view, на какое-либо действие от пользователя, вызывают так называемые actions, которые выполняют обработку этих действий (выполняют/получают реквесты на сервер, бизнес логику и т.д.) и новые данные вместе с флагом отдают reducers, которые знают, как применить эти данные к состоянию приложения.
Таким образом у нас получается разделение ответственности и однонаправленный поток данных, что удобно поддерживать в дальнейшем и покрывать тестами.
Пока, возможно, это звучит не очень понятно, но на примере получения/отображения ленты сообщений мы разберем архитектуру подробнее.
В директории app/blog/ создадим redux-инфраструктурные файлы. blogActions.jsx, blogReducer.jsx, blogConstants.jsx (для хранения ключей действий).
Перейдем в blogActions.jsx и напишем метод получения списка постов с сервера.
С помощью метода getPosts мы получаем данные, а с помощью метода state.dispath() мы сообщаем reducers о том, что произошло действие и результатом стали некие данные. В нашем случае действия это recievePosts и errorReceive. GET_POSTS_SUCCESS, GET_POSTS_ERROR, это константы по которым reducer будет идентифицировать действие.
Теперь перейдем в blogReducer.jsx и напишем код, который изменяет состояние нашего приложения по этим действиям.
Все просто, метод-reducer получает на вход текущее состояние и действие. По switch определяет какое действие произошло, изменяет состояние и возвращает новую его копию. initialState, как не трудно догадаться, состояние по умолчанию.
Ок, теперь нам нужно собрать все это воедино и заставить работать.
Перейдем в файл blog.jsx, накидаем разметку и подключим нашу redux инфраструктуру.
1. Класс Blog, с простой разметкой, который при инициализации запрашивает посты и отображает их.
2. Функцию mapProps, которая маппит состояние приложения на переменные-параметры.
3. Функцию mapDispath, которая маппит action на переменные-методы.
4. Функция connect, которая оборачивает класс-компонент Blog в redux-инфраструктуру и передает ему замапленные параметры в – this.props, с которыми мы уже и работает в самом компоненте.
Чтобы все окончательно заработало нам необходимо обернуть наше приложение в react-redux компонент Provider и создать хранилище приложения store.
Теперь добавим несколько записей-постов в базу и протестируем, что у нас получилось.
Я не буду рассматривать создание остальных компонентов, так как пишутся они по аналогии с компонентом блога, который мы рассмотрели — создаются actions/reducer и все это применяется методом connect к react-компоненту.
Единственно стоит упомянуть, что для создания хранилища createStore, требуется передать один редьюсер, но мы можем(и обязательно захотим для удобства) разбить общий редьюсер на редьюсеры по фичам, и чтобы store корректно создавался необходимо будет их потом объединить. В этом нам поможет метод combineReducers.
Часть 3. Аутентификация
Последняя тема, которую мы кратко рассмотрим в этой статье будет аутентификация. Будем использовать популярную аутентификацию на основе JSON Web Token (JWT).
Принцип действия прост:
1. Мы передаем логин пароль серверу
2. Сервер, в случае если они корректны, генерирует токен, который включает в себя данные необходимые для последующей авторизации сервером и возвращает его клиенту.
3. Клиент сохраняет токен, например в localStorage и при каждом запросе, требующем авторизацию, прикрепляет токен к хедеру запроса.
4. Сервер проверяет токен на корректность, просрочку, если все ОК, то возвращает данные.
Поставим соответствующий nuget пакет для asp.net core — Microsoft.AspNetCore.Authentication.JwtBearer, или убедимся, что он у нас стоит вместе Microsoft.AspNetCore.All.
Регистрируем в сервисах аутентификацию и укажем схему аутентификации на основе JWT токенов. Следующим экстеншен методом (AddJwtBearer) конфигурируем ее.
Встраиваем аутентификацию в конвейер обработки запросов.
Добавляем API метод генерации токена. В GetIdentity мы идем в базу (или куда либо еще где у нас лежат пользователи), сверяем логин/пароль, если все ок, то создаем токен и возвращаем пользователю, иначе возвращаем 401 ошибку.
На клиенте, перед каждым запросом, который требует авторизации, необходимо добавлять заголовок в формате ‘Bearer’ + token
Заключение
Статья получилась несколько больше чем я рассчитывал, но несмотря на это она не затрагивает некоторые важные вещи, необходимые для разработки. Это конечно логи, написание тестов, как на бэке, так и на клиенте, библиотеки для этого, серверный рендеринг клиентских компонентов, CI/CD и еще кучу всего. Но все же надеюсь то, что я написал в этой статье, кому-то будет полезно и найдет свою целевую аудиторию.
Приложение, которое мы писали всю эту статью можно посмотреть ТУТ.
Исходники можно скачать с github.