Scala for Backend Engineering & Functional Programming · Lekcja

Endpointy JSON

Połączy Pan/Pani http4s z Circe.

Lekcja 3 z 413 kroki

Endpointy JSON to bezpłatna lekcja Scala for Backend Engineering & Functional Programming na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Scala for Backend Engineering & Functional Programming, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Scala for Backend Engineering & Functional Programming zawiera 4 lekcji w sumie.

JSON przez circe

http4s integruje JSON za pośrednictwem modułu http4s-circe, który łączy kodeki circe z EntityDecoder i EntityEncoder. circe to de facto standardowa funkcyjna biblioteka JSON dla języka Scala.

Definiuje się instancje Encoder i Decoder dla swoich typów, a http4s zajmuje się formatem przesyłanych danych.

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

Automatyczne wyprowadzanie kodeków

Za pomocą circe-generic można automatycznie wyprowadzać kodeki przy użyciu deriveEncoder/deriveDecoder albo adnotacji @JsonCodec. Mapują one pola klas przypadków na klucze JSON według nazw.

Wyprowadzanie wymaga kodeków dla typów wszystkich pól, rekurencyjnie.

import io.circe.generic.semiauto._

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

EntityEncoder z circe

Import org.http4s.circe.CirceEntityEncoder._ udostępnia niejawną instancję EntityEncoder[F, A] dla każdego A, dla którego istnieje circe Encoder. Następnie Ok(user) serializuje dane do JSON z użyciem application/json.

W procedurach obsługi nie trzeba ręcznie konwertować danych do Json.

import org.http4s.circe.CirceEntityEncoder._

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

EntityDecoder z circe

Analogicznie: org.http4s.circe.CirceEntityDecoder._ udostępnia EntityDecoder[F, A] dla każdego A, dla którego istnieje circe Decoder. Następnie req.as[User] analizuje ciało JSON.

Oba importy są dostępne razem pod nazwą CirceEntityCodec._.

import org.http4s.circe.CirceEntityDecoder._

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

Pełne żądanie POST z JSON

Połączenie dekodowania i kodowania pozwala utworzyć przejrzysty endpoint tworzenia: należy przeanalizować przychodzący JSON do typu domenowego, wykonać logikę biznesową, a następnie zakodować wynik z powrotem jako JSON.

Błędy dekodowania są sygnalizowane jako 422 lub 400 przez dekoder circe.

import org.http4s.circe.CirceEntityCodec._

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

Interpolacja json

W przypadku doraźnego tworzenia JSON interpolator napisów json biblioteki circe z io.circe.literal bezpośrednio tworzy wartość Json wraz z interpolowanymi wartościami Scali.

Jest przydatny w przypadku małych odpowiedzi lub danych testowych, gdy nie chce się tworzyć klasy przypadku.

import io.circe.literal._

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

Niestandardowe nazwy pól

Gdy klucze JSON różnią się od nazw pól Scali, można wyprowadzić kodek za pomocą Configuration z circe-generic-extras, na przykład dla snake_case, albo napisać kodek ręcznie z użyciem forProduct2.

Dzięki temu kontrakt API jest niezależny od wewnętrznego nazewnictwa.

import io.circe.Encoder

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

Kodowanie list

circe automatycznie udostępnia kodeki dla typów List, Vector, Option i Map, gdy tylko istnieje kodek elementów. Dlatego zwracanie kolekcji działa od razu.

Pusta lista jest kodowana jako [], a None jako nieobecny klucz albo null.

import org.http4s.circe.CirceEntityEncoder._

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

Walidowanie zdekodowanego JSON

Dekodowanie daje syntaktycznie poprawną wartość, ale za walidację semantyczną odpowiada programista. Po wykonaniu as należy przeprowadzić sprawdzenia, a w przypadku niepoprawnych danych zakończyć działanie za pomocą BadRequest.

Dla przejrzystości warto umieszczać niezmienniki domenowe w inteligentnych konstruktorach zwracających Either.

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

Odpowiedzi błędów jako JSON

Warto zwracać błędy strukturalne jako JSON, aby klienci mogli je analizować. Należy zdefiniować klasę przypadku błędu z koderem circe i przekazać ją do odpowiedniego konstruktora statusu.

Spójna struktura błędów znacznie ułatwia korzystanie z API.

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

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

Strumieniowanie JSON

W przypadku dużych kolekcji można strumieniować JSON zamiast buforować całość. jsonEncoderOf wraz z fs2 Stream[F, A] emituje tablicę JSON przyrostowo.

Dzięki temu zużycie pamięci pozostaje stałe dla dużych zbiorów wyników, z wykorzystaniem mechanizmu back-pressure biblioteki fs2.

import org.http4s.circe.streamJsonArrayEncoder

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

Szybkie sprawdzenie

Proszę przypomnieć sobie, który import umożliwia dekodowanie ciała JSON do klasy przypadku.

Podsumowanie

Skonfigurowano JSON za pomocą http4s-circe: wyprowadzono kodeki, zaimportowano CirceEntityCodec i użyto Ok(value) / req.as[A] do pełnej obsługi wejścia i wyjścia JSON.

Obsłużono listy, niestandardowe nazwy pól, strukturalne błędy JSON, a nawet strumieniowane tablice dla dużych danych.

Bezpłatny start

Ucz się Scala dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
39
Lekcje
143

Często zadawane pytania

Czy lekcja „Endpointy JSON” jest bezpłatna?

Tak — pełny tekst „Endpointy JSON” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Scala for Backend Engineering & Functional Programming, przejdź na CoddyKit PRO. Kurs Scala for Backend Engineering & Functional Programming zawiera 4 lekcji w sumie.

Co nauczysz się w „Endpointy JSON”?

Połączy Pan/Pani http4s z Circe. Ćwiczysz Scala for Backend Engineering & Functional Programming z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Scala for Backend Engineering & Functional Programming?

Nie wymagamy żadnego doświadczenia. Scala for Backend Engineering & Functional Programming w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Endpointy JSON”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Scala for Backend Engineering & Functional Programming?

Tak. Każda lekcja Scala for Backend Engineering & Functional Programming zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Trasy i HttpRoutes
  2. Żądania i odpowiedzi
  3. Endpointy JSON
  4. Udostępnianie aplikacji
← Powrót do Scala for Backend Engineering & Functional Programming