Crystal Programming. Введение на основе проекта в создание эффективных, безопасных и читаемых веб-приложений и приложений CLI - Джордж Дитрих
Шрифт:
Интервал:
Закладка:
Преобразователь определяет тип, в который он должен десериализоваться, на основе ограничения типа соответствующего параметра метода. Если этот тип не включает JSON::Serializable или ASR::Serializable, выдается ошибка времени компиляции. Мы можем подтвердить, что все еще работает, сделав еще один запрос, подобный предыдущему, и утверждая, что мы получили тот же ответ, что и раньше.
Однако есть проблема с этой реализацией. Наш API в настоящее время с радостью принимает пустые значения как для свойств заголовка, так и для тела. Вероятно, нам следует предотвратить это, проверив тело запроса, чтобы мы могли быть уверены в его корректности к тому моменту, когда оно дойдет до действия контроллера. К счастью для нас, мы можем использовать компонент Validator Athena.
Проверка
Компонент Athena Validator — это надежная и гибкая среда для проверки как объектов, так и значений. Его основной API предполагает применение аннотаций, представляющих ограничения, которые вы хотите проверить. Экземпляр этого объекта затем может быть проверен с помощью экземпляра валидатора, который вернет, возможно, пустой список нарушений. У компонента слишком много функций, чтобы их можно было охватить в этой главе, поэтому мы сосредоточимся на том, что необходимо для проверки наших статей. См. https://athenaframework.org/Validator/ для получения дополнительной информации.
Что касается наших статей, то главное, чего мы хотим избежать, — это пустые значения. Мы также можем ввести требования к минимальной и максимальной длине, гарантируя, что они не содержат определенных слов или фраз или чего-либо еще, что вы захотите сделать. В любом случае, первое, что нужно сделать, — это включить AVD::Validatable в наш тип Article. Отсюда мы можем затем применить ограничение NotBlank к заголовку и телу, добавив аннотацию @[Assert::NotBlank], например:
@[Assert::NotBlank]
property title : String
@[Assert::NotBlank]
property body : String
Если вы попытаетесь использовать пустые значения POST, будет возвращен ответ об ошибке 422, в котором будут указаны нарушения и свойство, к которому они относятся. UUID кода ошибки — это машиночитаемое представление конкретного нарушения, которое можно использовать для проверки определенных ошибок без необходимости анализа сообщения, которое можно настроить, например:
{
"code": 422,
"message": "Validation failed",
"errors": [
{
"property": "body",
"message": "This value should not be blank.",
"code": "0d0c3254-3642-4cb0-9882-46ee5918e6e3"
}
]
}
Это работает «из коробки», поскольку ATH::RequestBodyConverter проверит, является ли десериализованный объект проверяемым после его десериализации, и проверит его, если это так. Компонент валидатора имеет множество ограничений, но также можно определить собственные. См. https://athenaframework.org/Validator/Constraints/ и https://athenaframework.org/comComponents/validator/#custom-constraints для получения дополнительной информации соответственно.
Следующим в списке вопросов, на которые следует обратить внимание, является то, что в настоящее время наша конечная точка для создания статьи по сути просто возвращает то, что ей было предоставлено. Чтобы можно было просмотреть все статьи, нам нужно настроить возможность сохранения их в базе данных.
Реализация взаимодействия с базой данных
Любому приложению, которому необходимо сохранять данные, чтобы их можно было получить позже, необходима база данных той или иной формы. Наш блог ничем не отличается, поскольку нам понадобится способ хранения статей, составляющих блог. Существуют различные типы баз данных, такие как NoSQL или реляционные и другие, каждая из которых имеет свои плюсы и минусы. В нашем блоге мы собираемся упростить задачу и использовать реляционную базу данных, такую как MySQL или PostgreSQL. Не стесняйтесь использовать базу данных по вашему выбору, которая лучше всего соответствует потребностям вашего приложения, но для целей этой главы я буду использовать PostgreSQL.
Настройка базы данных
Crystal предоставляет сегмент абстракции базы данных https://github.com/crystallang/crystal-db, который определяет высокоуровневый API для взаимодействия с базой данных. Каждая реализация базы данных использует это в качестве основы и реализует способ получения данных из базового хранилища. Это обеспечивает унифицированный API и общие функции, которые могут использовать все реализации баз данных. В нашем случае мы можем использовать https://github.com/will/crystal-pg для взаимодействия с нашей базой данных PG.
Давайте начнем с добавления этой зависимости в раздел зависимостей shard.yml, который теперь должен выглядеть следующим образом:
dependencies:
athena:
github: athena-framework/framework
version: ~> 0.16.0
pg:
github: will/crystal-pg
version: ~> 0.26.0
Обязательно запустите shards install еще раз и добавьте require "pg" в src/blog.cr. При этом будет установлен сегмент абстракции базы данных Crystal вместе с драйвером для Postgres. Crystal также имеет несколько ORM, которые можно использовать для простого взаимодействия с базой данных. Однако для наших целей я собираюсь просто использовать абстракции базы данных по умолчанию, чтобы упростить задачу. ORM, по сути, являются обертками того, что предоставляется драйвером, поэтому полезно иметь представление о том, как они работают под капотом.
Базовый сегмент абстракции предоставляет модуль DB::Serializable, который мы можем использовать это, чтобы немного облегчить себе жизнь. Этот модуль работает аналогично JSON::Serializable, но для запросов к базе данных, что позволяет нам создавать экземпляр нашего типа из сделанного нами запроса. Стоит отметить, что этот модуль не сохраняет экземпляр в базу данных, а только читает из нее. Поэтому нам придется справиться с этим самостоятельно или, возможно, даже реализовать некоторые из наших собственных абстракций.
Прежде чем мы приступим к настройке регистрации пользователей, нам необходимо настроить базу данных. Есть несколько способов сделать это, но самый простой, который я нашел, — это использовать docker-compose, который позволит нам развернуть сервер Postgres, которым будет легко управлять, и при необходимости его можно будет отключить. Файл compose, который я использую, выглядит следующим образом:
version: '3.8'
services:
pg:
image: postgres:14-alpine
container_name: pg
ports:
- "5432:5432"
environment:
- POSTGRES_USER=blog_user
- POSTGRES_PASSWORD=mYAw3s0meB!log
volumes:
- pg-data:/var/lib/postgresql/data
- ./db:/migrations
volumes:
pg-data:
Хотя я не буду вдаваться в подробности, суть в том, что мы определяем контейнер pg, который будет использовать Postgres 14, доступный через порт по умолчанию, используя переменные среды для настройки пользователя и базы данных. и, наконец, создание тома, который позволит данным сохраняться между его запуском и выключением. Мы также добавляем папку db/ в качестве тома. Это сделано