Skip to content

Instantly share code, notes, and snippets.

@okapies okapies/UserClient.scala
Last active Dec 4, 2017

Embed
What would you like to do?
akka-http and JSON

JSON handling in akka-http.

Marshaller and Unmarshaller

3rd parties

akka-http-json

Custom JSON (un)marshaller for akka-http by using 3rd party libraries. The community seems to be very active.

akka-http + Jackson

akka-http + circe

circe's decode[A] produces Either[Error, A] but akka-http requires Future[A]. _.fold(Future.failed, Future.successful) can be a solution to convert from Either[Throwable, A] to Future[A].

Documents (in Japanese)

import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.{HttpMethods, HttpRequest, StatusCodes, Uri}
import akka.http.scaladsl.unmarshalling.{FromEntityUnmarshaller, Unmarshal}
import akka.stream.ActorMaterializer
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
object UserService {
case class User(id: Long, name: String, firstName: String, lastName: String, location: Location)
case class Location(country: String, city: String)
}
trait UserServiceJsonSupport extends FailFastCirceSupport {
import UserService._
import io.circe.Decoder
// enable automatic decoder derivation
import io.circe.generic.auto._
/**
* ```
* {
* "user": {
* "id": 1,
* "name": "okapies",
* "first_name": "Yuta",
* "last_name": "Okamoto",
* "location": {
* "country": "Japan",
* "city": "Tokyo"
* }
* }
* }
* ```
*/
implicit val tokenInfoDecoder: Decoder[User] =
Decoder.forProduct5("id", "name", "first_name", "last_name", "location")(User).prepare(_.downField("user"))
// Note: The decoder for Location will be automatically derived by circe.
}
class UserServiceClient(val endpoint: Uri)
(implicit val system: ActorSystem,
val materializer: ActorMaterializer,
val executionContext: ExecutionContext) extends UserServiceJsonSupport {
import UserService._
def getUser(username: String): Future[User] =
doRequest[User](HttpRequest(uri = Uri.parseAndResolve(s"/api/v1/users/$username", base = endpoint)))
private[this] def doRequest[A: FromEntityUnmarshaller](request: HttpRequest): Future[A] =
Http().singleRequest(request).transformWith {
case Success(res) if res.status == StatusCodes.OK =>
Unmarshal(res.entity).to[A]
case Success(res) =>
res.discardEntityBytes()
Future.failed(new Exception(s"HttpRequest failed: $res"))
case Failure(t) =>
Future.failed(t)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.