Ajax POST 请求和 Array[Byte] 响应
Ajax POST request with Array[Byte] response
我收到一个 ajax POST 请求,需要将正文数据作为 ByteBuffer 处理,并使用 Http4s (0.23.7) 以 Array[Byte] 响应。这是到目前为止我已经能够将所有东西放在一起,虽然它还没有工作:
// http4s/fs2/cats imports... +
import java.nio.ByteBuffer
import java.nio.channels.Channels
HttpRoutes.of[IO] {
case ajaxRequest@POST -> Root / "ajax" / path =>
val stream: fs2.Stream[IO, IO[Array[Byte]]] =
ajaxRequest.body
// 1. Convert body to InputStream (to enable converting to ByteBuffer)
.through(fs2.io.toInputStream)
.map { inputStream =>
// 2. Create and populate ByteBuffer
val byteBuffer = ByteBuffer.allocate(inputStream.available)
Channels.newChannel(inputStream).read(byteBuffer)
// 3. Process byteBuffer with library code
val futResult: Future[Array[Byte]] = getRpcResult(path, byteBuffer)
// 4. Convert Future to IO (?)
val ioResult: IO[Array[Byte]] = IO.fromFuture(IO(futResult))
ioResult
}
// 5. Convert stream to Response - how?
Ok(stream)
// 6. Set octet-stream content type
.map(_.withContentType(
`Content-Type`(MediaType.application.`octet-stream`, Charset.`UTF-8`)
).putHeaders(`Cache-Control`(NonEmptyList.of(`no-cache`()))))
}
一些问题:
- Http4s/fs2是否有一些完全不同的方法?
- 是否有更简单的方法将 ajaxRequest.body 转换为 ByteBuffer(步骤 1-2)?
- 我是否需要将 Future 转换为 IO(第 4 步)?
- 如何转换流以用作 Ok 响应(第 5 步)?
作为参考,以下是我在 Play 和 Akka-Http 中的做法:
// Play
def ajax(path: String): Action[RawBuffer] = {
Action.async(parse.raw) { implicit ajaxRequest =>
val byteBuffer = ajaxRequest.body.asBytes(parse.UNLIMITED).get.asByteBuffer
getRpcResult(path, byteBuffer).map(Ok(_))
}
}
// Akka-Http
path("ajax" / Remaining)(path =>
post {
extractRequest { req =>
req.entity match {
case HttpEntity.Strict(_, byteString) =>
complete(getRpcResult(path, byteString.asByteBuffer))
}
}
}
)
提前感谢您的任何建议!
下面的代码是我脑子里写的,跟在文档后面,因为我现在不能测试它。
(因此它可能有一些拼写错误/错误;如果您找到一个,请随时编辑答案,谢谢!)
HttpRoutes.of[IO] {
case ajaxRequest @ POST -> Root / "ajax" / path =>
val response =
ajaxRequest.as[Array[Byte]].flatMap { byteArray =>
IO.fromFuture(IO(
getRpcResult(path, ByteBuffer.wrap(byteArray))
))
}
Ok(response)
}
让我们展开一些事情。
- http4s 支持读写
Array[Byte]
开箱即用,所以不需要做有趣的事情。
- 是的,你需要把一个
Future
转换成IO
;你的做法是错误的,因为 Future
已经失控了。另外,如果你想知道 IO
是什么,它解决了什么问题以及为什么 http4s 不使用 Future
;你甚至不会问这个问题。
- 您应该在适当的界面中隐藏
Future
。这更多的是一般设计建议,而不是 http4s 具体建议。这不仅会使代码更漂亮,而且以后更容易替换遗留模块或在测试中使用伪造的实现。
无论如何,通过查看您的代码和您提出的问题,很明显您不熟悉 typelevel 堆栈,也不熟悉 "Programs as Values" paradigm。
因此,首先,我鼓励您加入 Typelevel Discord server,因为通过该平台可能比在 Whosebug 上更容易帮助您。
其次,我建议你问问自己为什么要使用那个堆栈?是为了自学吗?那么好,但是,请遵循适当的学习路径,而不是只是尝试随机代码(毕竟,这是一个全新的编程范式);是为了工作?好的,但是,再一次,寻求支持,因为如果没有适当的指导,你会撞到另一堵墙,你最终会讨厌这个美妙的生态系统;是不是另有原因?话又说回来,想一想主要的驱动因素是什么,并遵循适当的计划。
此外,您可能想看看 fs2-grpc 之类的东西是否有用,这样您就不需要在内存中缓冲所有请求/响应,而是以两种方式流式传输所有内容。
我收到一个 ajax POST 请求,需要将正文数据作为 ByteBuffer 处理,并使用 Http4s (0.23.7) 以 Array[Byte] 响应。这是到目前为止我已经能够将所有东西放在一起,虽然它还没有工作:
// http4s/fs2/cats imports... +
import java.nio.ByteBuffer
import java.nio.channels.Channels
HttpRoutes.of[IO] {
case ajaxRequest@POST -> Root / "ajax" / path =>
val stream: fs2.Stream[IO, IO[Array[Byte]]] =
ajaxRequest.body
// 1. Convert body to InputStream (to enable converting to ByteBuffer)
.through(fs2.io.toInputStream)
.map { inputStream =>
// 2. Create and populate ByteBuffer
val byteBuffer = ByteBuffer.allocate(inputStream.available)
Channels.newChannel(inputStream).read(byteBuffer)
// 3. Process byteBuffer with library code
val futResult: Future[Array[Byte]] = getRpcResult(path, byteBuffer)
// 4. Convert Future to IO (?)
val ioResult: IO[Array[Byte]] = IO.fromFuture(IO(futResult))
ioResult
}
// 5. Convert stream to Response - how?
Ok(stream)
// 6. Set octet-stream content type
.map(_.withContentType(
`Content-Type`(MediaType.application.`octet-stream`, Charset.`UTF-8`)
).putHeaders(`Cache-Control`(NonEmptyList.of(`no-cache`()))))
}
一些问题:
- Http4s/fs2是否有一些完全不同的方法?
- 是否有更简单的方法将 ajaxRequest.body 转换为 ByteBuffer(步骤 1-2)?
- 我是否需要将 Future 转换为 IO(第 4 步)?
- 如何转换流以用作 Ok 响应(第 5 步)?
作为参考,以下是我在 Play 和 Akka-Http 中的做法:
// Play
def ajax(path: String): Action[RawBuffer] = {
Action.async(parse.raw) { implicit ajaxRequest =>
val byteBuffer = ajaxRequest.body.asBytes(parse.UNLIMITED).get.asByteBuffer
getRpcResult(path, byteBuffer).map(Ok(_))
}
}
// Akka-Http
path("ajax" / Remaining)(path =>
post {
extractRequest { req =>
req.entity match {
case HttpEntity.Strict(_, byteString) =>
complete(getRpcResult(path, byteString.asByteBuffer))
}
}
}
)
提前感谢您的任何建议!
下面的代码是我脑子里写的,跟在文档后面,因为我现在不能测试它。
(因此它可能有一些拼写错误/错误;如果您找到一个,请随时编辑答案,谢谢!)
HttpRoutes.of[IO] {
case ajaxRequest @ POST -> Root / "ajax" / path =>
val response =
ajaxRequest.as[Array[Byte]].flatMap { byteArray =>
IO.fromFuture(IO(
getRpcResult(path, ByteBuffer.wrap(byteArray))
))
}
Ok(response)
}
让我们展开一些事情。
- http4s 支持读写
Array[Byte]
开箱即用,所以不需要做有趣的事情。 - 是的,你需要把一个
Future
转换成IO
;你的做法是错误的,因为Future
已经失控了。另外,如果你想知道IO
是什么,它解决了什么问题以及为什么 http4s 不使用Future
;你甚至不会问这个问题。 - 您应该在适当的界面中隐藏
Future
。这更多的是一般设计建议,而不是 http4s 具体建议。这不仅会使代码更漂亮,而且以后更容易替换遗留模块或在测试中使用伪造的实现。
无论如何,通过查看您的代码和您提出的问题,很明显您不熟悉 typelevel 堆栈,也不熟悉 "Programs as Values" paradigm。
因此,首先,我鼓励您加入 Typelevel Discord server,因为通过该平台可能比在 Whosebug 上更容易帮助您。
其次,我建议你问问自己为什么要使用那个堆栈?是为了自学吗?那么好,但是,请遵循适当的学习路径,而不是只是尝试随机代码(毕竟,这是一个全新的编程范式);是为了工作?好的,但是,再一次,寻求支持,因为如果没有适当的指导,你会撞到另一堵墙,你最终会讨厌这个美妙的生态系统;是不是另有原因?话又说回来,想一想主要的驱动因素是什么,并遵循适当的计划。
此外,您可能想看看 fs2-grpc 之类的东西是否有用,这样您就不需要在内存中缓冲所有请求/响应,而是以两种方式流式传输所有内容。