http4s - 如何将GET请求的body作为字符串或输入流获取

11

我正在尝试定义一个HttpService,它接收JSON并使用json4s库将其解析为case 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.bodyreq.bodyAsText中获取org.json4s.JsonInput
我知道json4s还有StringInputStreamInput,它们都继承自JsonInput,用于处理StringInputStream类型的数据。所以我认为我需要将req.body转换为InputStream,或将req.bodyAsText转换为String,但我仍然不知道该如何实现。
我是Scala的新手,对于一些概念(例如scalaz.stream.Process)尚未完全理解。
3个回答

7
您可以使用http4s-json4s-jackson(或http4s-json4s-native)包,并使用org.http4s.EntityDecoder轻松从请求中获取Foo(我将您的Request case class重命名为Foo)。

EntityDecoder是一种类型类,可以从请求体中解码实体。
我们想要获得以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库可能是argonautcirce。因此,您可能会发现更多使用其中一种库的http4s示例。


6
彼得的解决方案既纠正了问题,又回答了它,但我在这里跌跤了,寻找OP所述但不是预期问题的解决方案:“如何在http4s中获取请求体作为[...] InputStream”。感谢GitHub上Issue 634中的讨论,这是我想出来的:
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)

1
你可以在调用 Http4s 服务之前使用 flatMapas 内部,以解码来自其响应的数据:
@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]将返回像这样的字符串。

"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接