http4s - 将请求体获取为 String 或 InputStream
http4s - get request body as String or InputStream
我正在尝试定义 HttpService
,它接收 json 并使用 json4s
库将其解析为大小写 class:
import org.http4s._
import org.http4s.dsl._
import org.json4s._
import org.json4s.native.JsonMethods._
case class Request(firstName: String, secondName: String)
HttpService {
case req @ POST -> Root =>
val request = parse(<map req.body or req.bodyAsText to JsonInput>).extract[Request]
Ok()
}
如何从 req.body
或 req.bodyAsText
得到 org.json4s.JsonInput
?
我知道 json4s
也有 StringInput
和 StreamInput
继承自 JsonInput
用于 String
和 InputStream
所以我认为我需要将 req.body
转换为 InputStream
或将 req.bodyAsText
转换为 String
但我仍然不明白如何。
我是 Scala 的新手,我还没有完全理解一些概念,例如 scalaz.stream.Process
。
您可以使用 http4s-json4s-jackson
(或 http4s-json4s-native
)包并使用 org.http4s.EntityDecoder
轻松获得 Foo
(我重命名了您的 Request
案例class 到 Foo
以下)来自请求。
EntityDecoder
是一种 class 类型,它可以从请求正文中解码实体。
我们想要得到 JSON 中的 Foo
,所以我们需要创建一个可以解码 JSON 的 EntityDecoder[Foo]
。如果我们想使用 json4s 创建这个解码器,我们需要 Reader
(或 JsonFormat
)。
如果您有一个 EntityDecoder[Foo]
实例,我们可以从带有 req.as[Foo]
的请求中获取 Foo
。
import org.json4s._
import org.json4s.jackson.JsonMethods._
import org.http4s._
import org.http4s.dsl._
import org.http4s.json4s.jackson._
case class Foo(firstName: String, secondName: String)
// create a json4s Reader[Foo]
implicit val formats = DefaultFormats
implicit val fooReader = new Reader[Foo] {
def read(value: JValue): Foo = value.extract[Foo]
}
// create a http4s EntityDecoder[Foo] (which uses the Reader)
implicit val fooDec = jsonOf[Foo]
val service = HttpService {
case req @ POST -> Root =>
// req.as[Foo] gives us a Task[Foo]
// and since Ok(...) gives a Task[Response] we need to use flatMap
req.as[Foo] flatMap ( foo => Ok(foo.firstName + " " + foo.secondName) )
}
注意:最常与 http4s 一起使用的 json 库可能是 argonaut and circe。因此,您可能会找到更多使用这些库之一的 http4s 示例。
Peter 的解决方案既纠正了问题又回答了问题,但我在这里偶然发现了 OP 提出但并非预期的问题的解决方案:"how to get request body as [...] InputStream" in http4s
。感谢 Issue 634 对 GitHub 的讨论,这是我想出的:
import java.io.InputStream
import org.http4s._
implicit val inputStreamDecoder: EntityDecoder[InputStream] =
EntityDecoder.decodeBy(MediaRange.`*/*`) { msg =>
DecodeResult.success(scalaz.stream.io.toInputStream(msg.body))
}
然后在您的 HttpService 中,像这样使用该解码器:
request.as[InputStream].flatMap { inputStream => ...inputStream is an InputStream... }
或者跳过整个解码器舞蹈,如果你愿意的话:
val inputStream = scalaz.stream.io.toInputStream(request.body)
在调用 Http4s 服务解码来自它的响应之前,您可以在其中使用 flatMap
和 as
:
@Test def `Get json gives valid contact`: Unit = {
val request = Request[IO](GET, uri"/contact")
val io = Main.getJsonWithContact.orNotFound.run(request)
// here is magic
val response = io.flatMap(_.as[Json]).unsafeRunSync()
val contact = contactEncoder(Contact(1, "Denis", "123")) // this is encoding to json for assertion
assertEquals(contact, response)
}
类型在这里是这样工作的:
val io: IO[Response[IO]] = Main.getJsonWithContact.orNotFound.run(request)
val response: IO[Json] = io.flatMap(_.as[Json])
val res: Json = response.unsafeRunSync()
as[String]
将 return 这样的字符串。
我正在尝试定义 HttpService
,它接收 json 并使用 json4s
库将其解析为大小写 class:
import org.http4s._
import org.http4s.dsl._
import org.json4s._
import org.json4s.native.JsonMethods._
case class Request(firstName: String, secondName: String)
HttpService {
case req @ POST -> Root =>
val request = parse(<map req.body or req.bodyAsText to JsonInput>).extract[Request]
Ok()
}
如何从 req.body
或 req.bodyAsText
得到 org.json4s.JsonInput
?
我知道 json4s
也有 StringInput
和 StreamInput
继承自 JsonInput
用于 String
和 InputStream
所以我认为我需要将 req.body
转换为 InputStream
或将 req.bodyAsText
转换为 String
但我仍然不明白如何。
我是 Scala 的新手,我还没有完全理解一些概念,例如 scalaz.stream.Process
。
您可以使用 http4s-json4s-jackson
(或 http4s-json4s-native
)包并使用 org.http4s.EntityDecoder
轻松获得 Foo
(我重命名了您的 Request
案例class 到 Foo
以下)来自请求。
EntityDecoder
是一种 class 类型,它可以从请求正文中解码实体。
我们想要得到 JSON 中的 Foo
,所以我们需要创建一个可以解码 JSON 的 EntityDecoder[Foo]
。如果我们想使用 json4s 创建这个解码器,我们需要 Reader
(或 JsonFormat
)。
如果您有一个 EntityDecoder[Foo]
实例,我们可以从带有 req.as[Foo]
的请求中获取 Foo
。
import org.json4s._
import org.json4s.jackson.JsonMethods._
import org.http4s._
import org.http4s.dsl._
import org.http4s.json4s.jackson._
case class Foo(firstName: String, secondName: String)
// create a json4s Reader[Foo]
implicit val formats = DefaultFormats
implicit val fooReader = new Reader[Foo] {
def read(value: JValue): Foo = value.extract[Foo]
}
// create a http4s EntityDecoder[Foo] (which uses the Reader)
implicit val fooDec = jsonOf[Foo]
val service = HttpService {
case req @ POST -> Root =>
// req.as[Foo] gives us a Task[Foo]
// and since Ok(...) gives a Task[Response] we need to use flatMap
req.as[Foo] flatMap ( foo => Ok(foo.firstName + " " + foo.secondName) )
}
注意:最常与 http4s 一起使用的 json 库可能是 argonaut and circe。因此,您可能会找到更多使用这些库之一的 http4s 示例。
Peter 的解决方案既纠正了问题又回答了问题,但我在这里偶然发现了 OP 提出但并非预期的问题的解决方案:"how to get request body as [...] InputStream" in http4s
。感谢 Issue 634 对 GitHub 的讨论,这是我想出的:
import java.io.InputStream
import org.http4s._
implicit val inputStreamDecoder: EntityDecoder[InputStream] =
EntityDecoder.decodeBy(MediaRange.`*/*`) { msg =>
DecodeResult.success(scalaz.stream.io.toInputStream(msg.body))
}
然后在您的 HttpService 中,像这样使用该解码器:
request.as[InputStream].flatMap { inputStream => ...inputStream is an InputStream... }
或者跳过整个解码器舞蹈,如果你愿意的话:
val inputStream = scalaz.stream.io.toInputStream(request.body)
在调用 Http4s 服务解码来自它的响应之前,您可以在其中使用 flatMap
和 as
:
@Test def `Get json gives valid contact`: Unit = {
val request = Request[IO](GET, uri"/contact")
val io = Main.getJsonWithContact.orNotFound.run(request)
// here is magic
val response = io.flatMap(_.as[Json]).unsafeRunSync()
val contact = contactEncoder(Contact(1, "Denis", "123")) // this is encoding to json for assertion
assertEquals(contact, response)
}
类型在这里是这样工作的:
val io: IO[Response[IO]] = Main.getJsonWithContact.orNotFound.run(request)
val response: IO[Json] = io.flatMap(_.as[Json])
val res: Json = response.unsafeRunSync()
as[String]
将 return 这样的字符串。