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

一些问题:

作为参考,以下是我在 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) 
}

让我们展开一些事情。

  1. http4s 支持读写 Array[Byte] 开箱即用,所以不需要做有趣的事情。
  2. 是的,你需要把一个Future转换成IO;你的做法是错误的,因为 Future 已经失控了。另外,如果你想知道 IO 是什么,它解决了什么问题以及为什么 http4s 不使用 Future;你甚至不会问这个问题。
  3. 您应该在适当的界面中隐藏 Future。这更多的是一般设计建议,而不是 http4s 具体建议。这不仅会使代码更漂亮,而且以后更容易替换遗留模块或在测试中使用伪造的实现。

无论如何,通过查看您的代码和您提出的问题,很明显您不熟悉 typelevel 堆栈,也不熟悉 "Programs as Values" paradigm

因此,首先,我鼓励您加入 Typelevel Discord server,因为通过该平台可能比在 Whosebug 上更容易帮助您。
其次,我建议你问问自己为什么要使用那个堆栈?是为了自学吗?那么好,但是,请遵循适当的学习路径,而不是只是尝试随机代码(毕竟,这是一个全新的编程范式);是为了工作?好的,但是,再一次,寻求支持,因为如果没有适当的指导,你会撞到另一堵墙,你最终会讨厌这个美妙的生态系统;是不是另有原因?话又说回来,想一想主要的驱动因素是什么,并遵循适当的计划。


此外,您可能想看看 fs2-grpc 之类的东西是否有用,这样您就不需要在内存中缓冲所有请求/响应,而是以两种方式流式传输所有内容。