简体   繁体   English

http4s - 以 String 或 InputStream 形式获取请求正文

[英]http4s - get request body as String or InputStream

I'm trying to define HttpService that receives json and parses it to case class with json4s library:我试图定义HttpService接收JSON并将其分析与案例类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()
}

How can I get org.json4s.JsonInput from req.body or req.bodyAsText ?我怎样才能得到org.json4s.JsonInputreq.bodyreq.bodyAsText

I know that json4s also have StringInput and StreamInput that inherits from JsonInput for using with String and InputStream so I think that I need to convert req.body to InputStream or req.bodyAsText to String but I still do not understand how.我知道json4s也有继承自JsonInput StringInputStreamInput用于与StringInputStream一起使用,所以我认为我需要将req.body转换为InputStreamreq.bodyAsText转换为String但我仍然不明白如何。

I'm new to Scala and I do not yet fully understand some concepts such as scalaz.stream.Process .我是 Scala 的新手,我还没有完全理解一些概念,例如scalaz.stream.Process

Peter's solution both corrects the question and answers it, but I stumbled here looking for the solution to OP's stated, but not intended, question: "how to get request body as [...] InputStream" in http4s .彼得的解决方案既纠正了问题,又回答了它,但我在这里偶然发现了 OP 提出但并非有意的问题的解决方案:“如何将请求正文作为 [...] InputStream” in http4s Thanks to the discussion in Issue 634 on GitHub, here's what I came up with:感谢 GitHub 上问题 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))
}

And then in your HttpService, use that decoder like so:然后在您的 HttpService 中,像这样使用该解码器:

request.as[InputStream].flatMap { inputStream => ...inputStream is an InputStream... }

Or skip the whole Decoder dance, if you want:或者跳过整个解码器舞蹈,如果你想:

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

You can use the http4s-json4s-jackson (or http4s-json4s-native ) packages and use an org.http4s.EntityDecoder to easily get a Foo (I renamed your Request case class to Foo below) from a request.您可以使用http4s-json4s-jackson (或http4s-json4s-native )包并使用org.http4s.EntityDecoder从请求中轻松获取Foo (我在下面将您的Request案例类重命名为Foo )。

EntityDecoder is a type class which can decode an entity from the request body. EntityDecoder是一个类型类,可以从请求正文中解码实体。 We want to get the Foo posted in JSON, so we need to create an EntityDecoder[Foo] which can decode JSON.我们想让Foo以 JSON EntityDecoder[Foo]发布,因此我们需要创建一个可以解码 JSON 的EntityDecoder[Foo] If we want to create this decoder using json4s we need a Reader (or a JsonFormat ).如果我们想使用 json4s 创建这个解码器,我们需要一个Reader (或一个JsonFormat )。

If you have an EntityDecoder[Foo] instance, we can get the Foo from the request with req.as[Foo] .如果你有一个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) )
}

Note: The json libraries libraries used most often with http4s are probably argonaut and circe .注意:最常与 http4s 一起使用的 json 库可能是argonautcirce So you might find more http4s examples using one of those libraries.因此,您可能会发现更多使用这些库之一的 http4s 示例。

You may use flatMap and as inside it before calling the Http4s service to decode responses from it:在调用 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)
}

This is how types work here:这就是类型在这里的工作方式:

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] will return the string just like this. as[String]将像这样返回字符串。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM