http4s - получить тело запроса в виде String или InputStream

Я пытаюсь определить 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()
}

Как я могу получить org.json4s.JsonInput от req.body или же req.bodyAsText?

я знаю это json4s Также есть StringInput а также StreamInput что наследует от JsonInput для использования с String а также InputStream так что я думаю что мне нужно конвертировать req.body в InputStream или же req.bodyAsText в String но я до сих пор не понимаю, как.

Я новичок в Scala и еще не до конца понимаю некоторые понятия, такие как scalaz.stream.Process,

2 ответа

Решение

Вы можете использовать http4s-json4s-jackson (или же http4s-json4s-native) пакеты и использовать org.http4s.EntityDecoder легко получить Foo (Я переименовал ваш Request кейс для Foo ниже) из запроса.

EntityDecoder является классом типа, который может декодировать объект из тела запроса. Мы хотим получить Foo размещены в формате JSON, поэтому нам нужно создать EntityDecoder[Foo] который может декодировать JSON. Если мы хотим создать этот декодер, используя 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) )
}

Примечание: библиотеки библиотек json, которые чаще всего используются с http4, вероятно, являются http://argonaut.io/ и circe. Таким образом, вы можете найти больше примеров http4s, используя одну из этих библиотек.

Решение Питера и исправляет вопрос, и отвечает на него, но я тут наткнулся на поиски решения поставленного, но не намеченного, вопроса OP: "как получить тело запроса как [...] InputStream" в http4s, Благодаря обсуждению в выпуске 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)

Вы можете использовать flatMap а также as внутри него перед вызовом службы Http4s для декодирования ответов от нее:

@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] вернет строку именно так.

Другие вопросы по тегам