【问题标题】:`Future.failed` inside of `transformWith[Array[Byte]]` gives a compiler error`transformWith[Array[Byte]]` 中的`Future.failed` 给出编译器错误
【发布时间】:2019-07-06 15:23:54
【问题描述】:

我正在尝试分析来自 Akka 的 HttpResponse。理想的行为是,如果响应成功返回,则传递 HttpEntityArray[Byte] 表示以进行处理。但是,如果状态返回失败,请传递带有异常的 Future.failed,其中包含状态代码和 HttpEntity 的 JSON 树表示。传递 JSON 树的原因是这个抽象请求方法会访问不同的服务器,它们的响应格式不同,所以我想在其他类中处理响应的解析。

我已尝试对此工作流程进行各种操作。直接抛出异常而不是返回 Future.failed 会返回 None 值来代替异常中的 JSON 树。其他方法产生类似的结果。当我println(MAPPER.readTree(byteArray)) 时,它会按我的预期打印出响应,但随后会在BadRequestExceptionresponse 字段中返回None

import akka.http.scaladsl.model._
import akka.http.scaladsl.model.headers.Authorization
import akka.stream.Materializer
import com.fasterxml.jackson.databind.{DeserializationFeature, JsonNode, ObjectMapper}
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper

val MAPPER = new ObjectMapper with ScalaObjectMapper
  MAPPER.registerModule(DefaultScalaModule)
  MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

def performQueryRaw(method: HttpMethod, uri: Uri, entity: Option[RequestEntity] = None, authorization: Option[Authorization] = None): Future[Array[Byte]] = {

  val request: HttpRequest = HttpRequest(
    method = method,
    uri = uri,
    entity = entity.getOrElse(HttpEntity.Empty),
    headers = authorization.toList)

  http.singleRequest(request).transformWith[Array[Byte]] {
    case Success(response: HttpResponse) =>
      convertEntityToBytes(response.entity).map { byteArray =>
        if (response.status.isFailure()) Future.failed(BadRequestException(response.status, MAPPER.readTree(byteArray)))
        else byteArray
      }
      case Failure(throwable) => Future.failed(RequestFailedException(throwable.getMessage + " -- " + uri.toString, throwable))
    }
  }

def convertEntityToBytes(entity: HttpEntity): Future[Array[Byte]] = {
  entity.dataBytes.runFold[Seq[Array[Byte]]] (Nil) {
    case (acc, next) => acc :+ next.toArray
  }.map(_.flatten.toArray)
}

case class BadRequestException(status: StatusCode, response: JsonNode = None.orNull, t: Throwable = None.orNull) extends Exception(t)

case class RequestFailedException(message: String, t: Throwable = None.orNull) extends Exception(message, t)

我期待 BadRequestException 输出 JsonNode 的非无值。相反,我在Future.failed 上收到编译器错误,内容如下:

Expression of type Future[Nothing] doesn't conform to expected type Array[Byte].

任何帮助将不胜感激。

【问题讨论】:

    标签: scala akka future akka-http jackson-databind


    【解决方案1】:

    convertEntityToBytes 之后运行下一步时,使用flatMap 而不是map

    def performQueryRaw(
        method: HttpMethod,
        uri: Uri,
        entity: Option[RequestEntity] = None,
        authorization: Option[Authorization] = None
      ): Future[Array[Byte]] = {
    
        val request: HttpRequest = HttpRequest(
          method = method,
          uri = uri,
          entity = entity.getOrElse(HttpEntity.Empty),
          headers = authorization.toList
        )
    
        Http().singleRequest(request).transformWith[Array[Byte]] {
          case Success(response: HttpResponse) =>
            convertEntityToBytes(response.entity).flatMap { byteArray =>
              if (response.status.isFailure()) Future.failed(new Exception("change this exception to one you had"))
              else Future.successful(byteArray)
            }
          case Failure(throwable) => Future.failed(new Exception("also here"))
        }
      }
    

    由于您希望 Future 计算失败,您需要返回新的 Future。万一失败,你已经在做Future.failed。缺少的部分是将 byteArray 包装到 Future.successful。当然这是解决此代码中类型编译错误的方法之一。

    【讨论】:

    • 我将此标记为解决我的问题,因为它解决了编译器错误。没有解决 MAPPER.readTree(byteArray) 在异常中为 null 的问题,但我设法自己解决了这个问题。我将response 转换为Option[JsonNode],默认为None。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-22
    • 1970-01-01
    • 2020-08-24
    相关资源
    最近更新 更多