http4s - 获取请求体作为String或InputStream

时间:2016-05-04 20:19:47

标签: scala scalaz-stream http4s

我试图定义接收json的HttpService并将其解析为带json4s库的案例类:

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()
}

如何从org.json4s.JsonInputreq.body获取req.bodyAsText

我知道json4s也有StringInputStreamInput继承自JsonInput以便与StringInputStream一起使用,所以我认为我需要将req.body转换为InputStreamreq.bodyAsText转换为String,但我仍然不明白如何。

我是Scala的新手,我还没有完全理解scalaz.stream.Process等一些概念。

3 个答案:

答案 0 :(得分:4)

彼得的解决方案都纠正了问题并回答了问题,但我在这里偶然发现了寻找OP的解决方案,但没有打算提出问题:&#34;如何获得请求正文[ ...] InputStream&#34;在http4s。感谢我在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... }

如果你愿意,可以跳过整个Decoder舞蹈:

val inputStream = scalaz.stream.io.toInputStream(request.body)

答案 1 :(得分:2)

您可以使用http4s-json4s-jackson(或http4s-json4s-native)个套件并使用org.http4s.EntityDecoder轻松获得Foo(我将您的Request案例类重命名为来自请求的Foo

EntityDecoder是一个类型类,可以从请求体中解码实体。 我们希望在JSON中发布Foo,因此我们需要创建一个可以解码JSON的EntityDecoder[Foo]。如果我们想使用json4s创建此解码器,我们需要Reader(或JsonFormat)。

如果您有EntityDecoder[Foo]个实例,我们可以通过Foo从请求中获取req.as[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) )
}

注意:最常用于http4的json库可能是argonautcirce。因此,您可以使用其中一个库找到更多http4s示例。

答案 2 :(得分: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]将像这样返回字符串。