Scala for Backend Engineering & Functional Programming · Урок

Конечные точки JSON

Объединяйте http4s с Circe.

Урок 3 из 413 шагов

«Конечные точки JSON» — бесплатный урок Scala for Backend Engineering & Functional Programming на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Scala for Backend Engineering & Functional Programming, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.

JSON с помощью circe

http4s интегрируется с JSON через модуль http4s-circe, который связывает кодеки circe с EntityDecoder и EntityEncoder. circe — фактический стандарт функциональной библиотеки JSON для Scala.

Вы определяете экземпляры Encoder и Decoder для своих типов, а http4s обрабатывает формат передачи.

// build.sbt
// "org.http4s" %% "http4s-circe" % http4sV
// "io.circe"   %% "circe-generic" % circeV

Выведение кодеков

С помощью circe-generic можно автоматически вывести кодеки, используя deriveEncoder/deriveDecoder или аннотацию @JsonCodec. Они сопоставляют поля классов вариантов с ключами JSON по именам.

Для выведения нужны кодеки всех типов полей, включая вложенные типы.

import io.circe.generic.semiauto._

case class User(id: Int, name: String)
implicit val enc = deriveEncoder[User]
implicit val dec = deriveDecoder[User]

EntityEncoder из circe

Импорт org.http4s.circe.CirceEntityEncoder._ предоставляет неявный EntityEncoder[F, A] для любого A, у которого есть Encoder circe. После этого Ok(user) сериализуется в JSON с типом содержимого application/json.

В обработчиках не требуется вручную преобразовывать данные в Json.

import org.http4s.circe.CirceEntityEncoder._

HttpRoutes.of[IO] {
  case GET -> Root / "me" => Ok(User(1, "Ada"))
}

EntityDecoder из circe

Аналогично, org.http4s.circe.CirceEntityDecoder._ предоставляет EntityDecoder[F, A] для любого A, у которого есть Decoder circe. После этого req.as[User] разбирает тело JSON.

Оба импорта вместе объединены в CirceEntityCodec._.

import org.http4s.circe.CirceEntityDecoder._

case req @ POST -> Root / "users" =>
  req.as[User].flatMap(u => Created(u))

Полный POST с JSON

Совместное декодирование и кодирование позволяет создать ясную конечную точку создания: разобрать входящий JSON в тип предметной области, выполнить бизнес-логику, а затем снова закодировать результат как JSON.

Ошибки декодирования через декодер circe проявляются как 422 или 400.

import org.http4s.circe.CirceEntityCodec._

case req @ POST -> Root / "users" =>
  for {
    in  <- req.as[User]
    out <- store.create(in)
    res <- Created(out)
  } yield res

Интерполятор json

Для разового создания JSON интерполятор строк json из io.circe.literal строит значение Json напрямую, поддерживая интерполяцию значений Scala.

Это удобно для небольших ответов или тестовых данных без класса вариантов.

import io.circe.literal._

val body = json"""{ "status": "ok", "count": 3 }"""
Ok(body)

Пользовательские имена полей

Если ключи JSON отличаются от имён полей Scala, выполняйте выведение с помощью Configuration из circe-generic-extras, например с настройкой snake_case, или напишите кодек вручную с помощью forProduct2.

Так контракт API отделяется от внутренних имён.

import io.circe.Encoder

implicit val e: Encoder[User] =
  Encoder.forProduct2("user_id", "full_name")(u => (u.id, u.name))

Кодирование списков

circe автоматически предоставляет кодеки для List, Vector, Option и Map, если кодек элемента уже существует. Поэтому возврат коллекции работает сразу.

Пустой список кодируется как [], а None — либо как отсутствующий ключ, либо как null.

import org.http4s.circe.CirceEntityEncoder._

case GET -> Root / "users" =>
  store.all.flatMap(us => Ok(us)) // List[User] -> JSON array

Проверка декодированного JSON

Декодирование даёт синтаксически корректное значение, но семантическая проверка остаётся на вашей стороне. Выполняйте проверки после as и прерывайте обработку с помощью BadRequest, если значение недопустимо.

Храните инварианты предметной области в интеллектуальных конструкторах, возвращающих Either, чтобы сделать код понятнее.

req.as[User].flatMap { u =>
  if (u.name.nonEmpty) Created(u)
  else BadRequest("name required")
}

Ошибки в виде JSON

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

Единообразная структура ошибок значительно упрощает использование API.

case class ApiError(code: String, message: String)
implicit val e = deriveEncoder[ApiError]

BadRequest(ApiError("E_NAME", "name required"))

Потоковая передача JSON

Для больших коллекций можно передавать JSON потоком, не буферизуя его целиком. jsonEncoderOf вместе с Stream[F, A] из fs2 постепенно выдаёт массив JSON.

Это сохраняет постоянное потребление памяти для больших наборов результатов благодаря обратному давлению fs2.

import org.http4s.circe.streamJsonArrayEncoder

case GET -> Root / "feed" =>
  Ok(store.streamAll) // fs2.Stream[IO, Event]

Быстрая проверка

Вспомните, какой импорт включает декодирование тела JSON в класс вариантов.

Итоги

Вы подключили JSON с помощью http4s-circe: вывели кодеки, импортировали CirceEntityCodec и использовали Ok(value) / req.as[A] для полноценного ввода-вывода JSON.

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

Можно начать бесплатно

Изучай Scala с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
39
Уроки
143

Часто задаваемые вопросы

Урок «Конечные точки JSON» бесплатный?

Да — полный текст урока «Конечные точки JSON» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Scala for Backend Engineering & Functional Programming, подпишись на CoddyKit PRO. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.

Чему я научусь в уроке «Конечные точки JSON»?

Объединяйте http4s с Circe. Ты практикуешь Scala for Backend Engineering & Functional Programming с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Scala for Backend Engineering & Functional Programming?

Предыдущий опыт не требуется. Scala for Backend Engineering & Functional Programming на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Конечные точки JSON»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Scala for Backend Engineering & Functional Programming?

Да. Каждый урок Scala for Backend Engineering & Functional Programming включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Маршруты и HttpRoutes
  2. Запросы и ответы
  3. Конечные точки JSON
  4. Предоставление приложения
← Назад к Scala for Backend Engineering & Functional Programming