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