io.circe
by Miguel Lopez
由Miguel Lopez
如何使用Circe(Un)在Akka HTTP中封送JSON (How to (Un)marshal JSON in Akka HTTP with Circe)
Even though the usual library to (un)marshal JSON in Akka HTTP applications is spray-json, I decided to give circe a try. I use it in the Akka HTTP beginners course I’m working on. *cough* it’s free ? *cough*
即使在Akka HTTP应用程序中用于(取消)封送JSON的常用库是spray-json,我还是决定尝试一下。 我正在研究的Akka HTTP初学者课程中使用它。 * 咳嗽 * 是免费的吗? * 咳嗽*
In this post I’d like to show you why I tried it out.
在这篇文章中,我想向您展示为什么我尝试了它。
To use circe with Akka HTTP — and other JSON libraries for that matter — we have to create the marshalers and unmarshalers manually. Thankfully, there is an additional library that already does that for us.
要将circe与Akka HTTP以及其他JSON库一起使用,我们必须手动创建封送程序和取消封送程序。 值得庆幸的是,已经有一个额外的库已经为我们做到了。
项目设置和概述 (Project setup and overview)
Clone the project’s repository, and checkout the branch 3.3-repository-implementation
.
克隆项目的存储库 ,然后检出branch 3.3-repository-implementation
。
Under src/main/scala
you'll find the following files:
在src/main/scala
您将找到以下文件:
$ tree srcsrc└── main └── scala ├── Main.scala ├── Todo.scala └── TodoRepository.scala2 directories, 3 files
The Main
object is the application's entry point. So far it has a hello world route and it binds it to a given host and route.
Main
对象是应用程序的入口点。 到目前为止,它具有问候世界路由,并将其绑定到给定的主机和路由。
Todo
is our application’s model. And the TodoRepository
is in charge of persisting and accessing todos. So far it only has an in-memory implementation to keep things simple and focused.
Todo
是我们应用程序的模型。 TodoRepository
负责持久化和访问待办事项。 到目前为止,它只有一个内存实现来使事情简单和集中。
列出所有待办事项 (Listing all the todos)
We’ll change the Main
object’s route to list all the todos in a repository. We will also add some initial todos for testing:
我们将更改Main
对象的路由以列出存储库中的所有待办事项。 我们还将添加一些初始待办事项进行测试:
import akka.actor.ActorSystemimport akka.http.scaladsl.Httpimport akka.stream.ActorMaterializerimport scala.concurrent.Awaitimport scala.util.{Failure, Success}object Main extends App { val host = "0.0.0.0" val port = 9000 implicit val system: ActorSystem = ActorSystem(name = "todoapi") implicit val materializer: ActorMaterializer = ActorMaterializer() import system.dispatcher val todos = Seq( Todo("1", "Clean the house", "", done = false), Todo("2", "Learn Scala", "", done = true), ) val todoRepository = new InMemoryTodoRepository(todos) import akka.http.scaladsl.server.Directives._ def route = path("todos") { get { complete(todoRepository.all()) } } val binding = Http().bindAndHandle(route, host, port) binding.onComplete { case Success(_) => println("Success!") case Failure(error) => println(s"Failed: ${error.getMessage}") } import scala.concurrent.duration._ Await.result(binding, 3.seconds)}
Now we’re listening to requests under /todos
and we respond with all the todos we have in our todoRepository
.
现在,我们正在侦听/todos
下的请求,并使用todoRepository
所有todo进行响应。
However, if we try to run this it won’t compile:
但是,如果我们尝试运行此命令,它将无法编译:
Error:(26, 34) type mismatch; found : scala.concurrent.Future[Seq[Todo]] required: akka.http.scaladsl.marshalling.ToResponseMarshallable complete(todoRepository.all())
The compilation error is telling us it doesn’t know how to marshal our todos into JSON.
编译错误告诉我们它不知道如何将待办事项编组为JSON。
We need to import circe and the support library:
我们需要导入circe和支持库:
import akka.http.scaladsl.server.Directives._import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport._import io.circe.generic.auto._def route = path("todos") { get { complete(todoRepository.all()) }}
With those two extra lines we can now run our Main
object and test our new route.
通过这两行,我们现在可以运行Main
对象并测试新路线。
Make a GET request to http://localhost:9000/todos
:
向http://localhost:9000/todos
发出GET请求:
And we get our todos back! ?
而且我们还可以得到待办事项! ?
创建待办事项 (Creating todos)
Turns out that unmarshaling JSON into our models doesn’t take much effort either. But our TodoRepository
doesn’t support saving todos at the moment. Let’s add that functionality first:
事实证明,将JSON编组到我们的模型中也不需要太多的工作。 但是我们的TodoRepository
目前不支持保存TodoRepository
。 让我们先添加该功能:
import scala.concurrent.{ExecutionContext, Future}trait TodoRepository { def all(): Future[Seq[Todo]] def done(): Future[Seq[Todo]] def pending(): Future[Seq[Todo]] def save(todo: Todo): Future[Todo]}class InMemoryTodoRepository(initialTodos: Seq[Todo] = Seq.empty)(implicit ec: ExecutionContext) extends TodoRepository { private var todos: Vector[Todo] = initialTodos.toVector override def all(): Future[Seq[Todo]] = Future.successful(todos) override def done(): Future[Seq[Todo]] = Future.successful(todos.filter(_.done)) override def pending(): Future[Seq[Todo]] = Future.successful(todos.filterNot(_.done)) override def save(todo: Todo): Future[Todo] = Future.successful { todos = todos :+ todo todo }}
We added a method save
to the trait and the implementation. Because we are using a Vector
our implementation of save
will store duplicated todos. That’s fine for the purposes of this tutorial.
我们向特征和实现添加了save
方法。 因为我们使用的是Vector
,所以save
的实现将存储重复的待办事项。 就本教程而言,这很好。
Let’s add a new route that will listen to POST requests. This route receive a Todo
as the request’s body and saves it into our repository:
让我们添加一条新的路由来监听POST请求。 此路由将Todo
作为请求的主体,并将其保存到我们的存储库中:
def route = path("todos") { get { complete(todoRepository.all()) } ~ post { entity(as[Todo]) { todo => complete(todoRepository.save(todo)) } }}
Using the entity
directive we can build a route that automatically parses incoming JSON to our model. It also rejects requests with invalid JSON:
使用entity
指令,我们可以构建一个路由,该路由自动将传入的JSON解析到我们的模型。 它还拒绝使用无效JSON的请求:
We sent the done
field as a string, and it should have been a boolean, which our API responded to with bad request.
我们以字符串形式发送了done
字段,它应该是一个布尔值,我们的API响应该请求时发出了错误请求。
Let’s make a valid request to create a new todo:
让我们发出一个有效的请求来创建一个新的待办事项:
This time we sent the property as done := false
, which tells HTTPie to send the value as Boolean
instead of String
.
这次我们将属性发送为done := false
,这告诉HTTPie将值作为Boolean
而不是String
。
We get our todo back and a 200 status code, which means it went well. We can confirm it worked by querying the todos again:
我们找回待办事项和200状态代码,这意味着一切顺利。 我们可以通过再次查询待办事项来确认它是否有效:
We get three todos, the hardcoded ones and the new one we created.
我们得到三个待办事项,分别是硬编码和新创建的。
结语 (Wrapping up)
We added JSON marshaling and unmarshaling to our application by adding the dependencies (it was already done in the project) and by importing both libraries.
通过添加依赖项(已经在项目中完成)并导入两个库,我们在应用程序中添加了JSON封送处理和取消封送处理。
Circe figures out how to handle our models without much intervention from us.
Circe指出了如何在没有太多干预的情况下处理我们的模型。
In a future post, we will explore how to accomplish the same functionality with spray-json instead.
在以后的文章中,我们将探索如何使用spray-json来实现相同的功能。
Stay tuned!
敬请关注!
If you liked this tutorial and want to learn how to build an API for a todo application, check out our new free course! ???
如果您喜欢本教程,并且想学习如何为待办事项应用程序构建API,请查看我们的新免费课程! ???
Akka HTTP QuickstartLearn how to create web applications and APIs with Akka HTTP in this free course!link.codemunity.io
Akka HTTP快速入门 在此免费课程中,了解如何使用Akka HTTP创建Web应用程序和API! link.codemunity.io
Originally published at www.codemunity.io.
最初在www.codemunity.io上发布。
翻译自: https://www.freecodecamp.org/news/un-marshalling-json-in-akka-http-with-circe-3dcc2764eedb/
io.circe