Выбрать главу

    entity.title,

    entity.body,

    entity.updated_at,

    entity.deleted_at,

    entity.id

  )

end

Отсюда мы можем обновить нашу конечную точку #update_article, чтобы она выглядела следующим образом:

@[ARTA::Put("/article/{id}")] @[ATHA::ParamConverter("article_entity", converter:

  Blog::Converters::Database)]

@[ATHA::ParamConverter("article", converter:

  ATH::RequestBodyConverter)]

def update_article(article_entity : Blog::Entities::Article,

  article : Blog::Entities::Article) : Blog::Entities::Article

  article_entity.title = article.title

  article_entity.body = article.body

  @entity_manager.persist article_entity

  article_entity

end

В этом примере мы используем два преобразователя параметров. Первый извлекает реальную сущность статьи из базы данных, а второй создает ее на основе тела запроса. Затем мы применяем статью тела запроса к сущности статьи и передаем ее в #persist. Допустим, мы делаем такой запрос:

curl --request PUT 'http://localhost:3000/article/1' \ --header 'Content-Type: application/json' \

--data-raw '{

  "title": "New Title",

  "body": "New Body",

  "updated_at": "2022-04-09T05:13:30Z",

  "created_at": "2022-04-09T04:47:09Z"

}'

Это приведет к такому ответу:

{

  "id": 1, "title": "New Title",

  "body": "New Body",

  "updated_at": "2022-04-09T05:22:44Z",

  "created_at": "2022-04-09T04:47:09Z"

}

Прекрасно! title, body, и updated_at были обновлены, как и ожидалось, тогда как временные метки id и create_at из базы данных не были изменены.

И последнее, но не менее важное: нам нужна возможность удалить статью.

Удаление статьи

Мы можем обрабатывать удаления, еще раз обновив наш менеджер сущностей, включив в него метод #remove, а также метод #on_remove для наших сущностей, который будет обрабатывать настройку свойства delete_at. Затем мы могли бы использовать преобразователь параметров базы данных на конечной точке DELETE и просто предоставить #remove разрешенному объекту.

Начните с добавления этого в менеджер сущностей:

def remove(entity : DB::Serializable) : Nil

  entity.on_remove if entity.responds_to? :on_remove

  self.update entity

end

А это к нашей статье:

protected def on_remove : Nil

  @deleted_at = Time.utc

end

Наконец, действие контроллера будет выглядеть так:

@[ARTA::Delete("/article/{id}")]

@[ATHA::ParamConverter("article", converter:

  Blog::Converters::Database)]

def delete_article(article : Blog::Entities::Article) : Nil

  @entity_manager.remove article

end

Затем мы могли бы сделать запрос, например, curl --request DELETE 'http:// localhost:3000/article/1' и увидеть в базе данных, что столбец delete_at установлен. Потому что метод #find? также отфильтровывает удаленные элементы, поэтому попытка удалить ту же статью еще раз приведет к ошибке 404.

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

Использование переговоров по содержанию

На данный момент наш блог действительно собирается вместе. Мы можем создавать, получать, обновлять и удалять статьи. У нас также есть несколько довольно надежных абстракций, которые помогут будущему росту. Как упоминалось ранее в этой главе, если действия контроллера напрямую возвращают объект, это может помочь в обработке нескольких форматов ответов. Например, предположим, что мы хотели расширить наше приложение, разрешив ему возвращать статью как в формате HTML, так и в формате JSON, в зависимости от заголовка принятия запроса.

Чтобы справиться с генерацией HTML, мы могли бы использовать встроенную функцию Crystal (ECR), которая по сути похожа на шаблонизацию во время компиляции. Однако было бы полезно иметь что-то более гибкое, похожее на PHP Twig, Python Jinja или Embedded Ruby (ERB). На самом деле существует кристальный порт Джинджи под названием Crinja, который мы можем использовать. Итак, сначала добавьте следующее в качестве зависимости к вашему shard.yml, обязательно запустив shards install и потребовав ее в src/blog.cr:

crinja:

  github: straight-shoota/crinja

  version: ~> 0.8.0

В Crinja есть модуль Crinja::Object, который можно включить, чтобы обеспечить доступ к определенным свойствам/методам этого типа в шаблоне. Он также имеет подмодуль Auto, который работает во многом аналогично JSON::Serializable. Поскольку это модуль, он также позволит нам проверить, доступен ли конкретный объект для визуализации, чтобы мы могли обработать случай ошибки при попытке отобразить объект, который невозможно отобразить.