使用 akka 编组的 scala 中的通用 return 类型

Generic return type in scala with akka marshalling

我得到了以下相当简单的代码:

case class InverterResponse(body: InverterBody) extends SolarResponse
case class InverterBody(data: InverterData)
case class InverterData(dayEnergy: DayEnergy)
case class DayEnergy(unit: String, values: Values)
case class Values(value: Int)

case class MeterResponse(body: MeterBody) extends SolarResponse
case class MeterBody(data: MeterData)
case class MeterData(powerRealSum: BigDecimal, powerRealPhase1: BigDecimal, powerRealPhase2: BigDecimal, powerRealPhase3: BigDecimal)

class SolarWebConnector extends JsonSupport {
  implicit val system = ActorSystem()
  implicit val materializer = ActorMaterializer()

  val httpClient = Http().outgoingConnection(host = "192.168.178.22", port = 80)

  def getInverterRealtimeData(): InverterData = {
    val inverterRealtimeURLPath = s"""/solar_api/v1/GetInverterRealtimeData.cgi?scope=System"""
    val flowGet: Future[InverterResponse] = sendRequest[InverterResponse](inverterRealtimeURLPath)
    val start = System.currentTimeMillis()
    val result = Await.result(flowGet, 5 seconds)
    val end = System.currentTimeMillis()
    println(s"Result in ${end - start} millis: $result")
    result.body.data
  }

  private def sendRequest[T](inverterRealtimeURLPath: String) : Future[T] = {
    val flowGet: Future[T] =
      Source.single(
        HttpRequest(
          method = HttpMethods.GET,
          uri = Uri(inverterRealtimeURLPath))
      )
        .via(httpClient)
        .mapAsync(1)(response => Unmarshal(response.entity).to[T])
        .runWith(Sink.head)
    flowGet
  }

  def getMeterRealtimeData(): String = {
    "test"
  }
}

JsonSupport 包含 json 格式定义以将传入 json 编组到响应案例 类。只要我不尝试为 sendRequest 方法定义一个通用版本,它就可以正常工作,它可以是 return InverterResponse 或 MeterResponse 类型。它编译得很好,但我得到:

Error:(54, 63) Play 2 Compiler: 
 /Users/tbecker/workspaces/home-integrator/app/services/SolarWebConnector.scala:54:63: could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.Unmarshaller[akka.http.scaladsl.model.ResponseEntity,T]
         .mapAsync(1)(response => Unmarshal(response.entity).to[T])
                                                               ^

大约 2 年没有真正使用过 scala...所以我的知识可能有点生锈...

通过深入研究资源和文档,我自己找到了答案。

https://doc.akka.io/docs/akka-http/current/scala/http/common/unmarshalling.html

https://github.com/akka/akka-http/blob/v10.0.3/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/Unmarshal.scala

Umarshall 需要一个隐式的 Unmarshaller。像这样在方法签名中提供它可以解决问题:

private def sendRequest[T](inverterRealtimeURLPath: String)(隐式 m: Unmarshaller[ResponseEntity, T]) : Future[T] = {