【问题标题】:Akka-HTTP compilation error using custom requireParam() directive使用自定义 requireParam() 指令的 Akka-HTTP 编译错误
【发布时间】:2017-11-20 14:12:21
【问题描述】:

我开发了自定义通用指令,它将提供给定类型的参数(如果存在),否则拒绝我的自定义异常。

import akka.http.scaladsl.common.NameReceptacle
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.directives.ParameterDirectives.ParamDefAux
import akka.http.scaladsl.server.{Directive1, Route}

class MyCustomException(msg: String) extends Exception(msg)

def requireParam[T](name: NameReceptacle[T])
                   (implicit pdef: ParamDefAux[NameReceptacle[T], Directive1[T]]): Directive1[T] =
  parameter(name).recover { _ =>
    throw new MyCustomException(s"${name.name} is missed!")
  }

如果我想创建路由,可以使用两个参数,例如:

val negSumParams: Route =
  (requireParam("param1".as[Int]) & requireParam("param2".as[Int])) {
    (param1, param2) =>
      complete((-param1-param2).toString)
  }

但如果我尝试只使用一个参数,则无法编译:

val negParamCompilationFail: Route =
  requireParam("param".as[Int]) {
    param => // scalac complains about missing type param here
      complete((-param).toString)
  }

如果我将它与pass 指令一起使用,它可以工作:

val negParamWithPass: Route =
  (pass & requireParam("param".as[Int])) { // this pass usage looks hacky
    param =>
      complete((-param).toString)
  }

如果我明确写出requireParam() 返回类型,它也可以:

val negParamWithExplicitType: Route =
  (requireParam("param".as[Int]): Directive1[Int]) { // DRY violation
    param =>
      complete((-param).toString)
  }

为什么我需要这些技巧?为什么不能只使用requireParam("param".as[Int])

Scala 版本 2.12.1,Akka-HTTP 10.0.10。

【问题讨论】:

    标签: scala akka-http


    【解决方案1】:

    这个错误是由于指令伴随对象应用方法引起的。 IT 允许从带有参数 (T => Route) => Route:

    的函数创建路由
    object Directive {
    
      /**
       * Constructs a directive from a function literal.
       */
      def apply[T: Tuple](f: (T ⇒ Route) ⇒ Route): Directive[T] =
        new Directive[T] { def tapply(inner: T ⇒ Route) = f(inner) }
    
    } 
    

    但是 T 参数必须是一个元组。在您的情况下,编译器无法构建路由。您的 requireParam("param".as[Int]) 返回一个 Directive1[Int] 因此 apply 方法不起作用,因为 Int 不是元组。

    要完成这项工作,您应该直接使用 tapply 方法:

    (requireParam("param1".as[Int])).tapply((param1) =>
        complete((-param1._1).toString))
    

      val negSumParams2: Route =
        (requireParam("param1".as[Int]) & requireParam("param2".as[Int])).tapply {
          case (param1, param2) =>
            complete((-param1-param2).toString)
        }
    

    因此,似乎每个指令都试图将其参数转换为 TupleX。例如:

    path("order" / IntNumber) 返回一个 Directive[Tuple1[Int]] 而不是 Directive1[Int]。在你的情况下 requireParam("param1".as[Int]) 返回 Directive1[Int]

    也许有更好的解决方案,避免轻拍

    【讨论】:

    • Directive1[Int] 只是Directive[Tuple1[Int]] 的别名。此外,这个编译:val dir = requireParam("param1".as[Int]); dir { param1 => complete((-param).toString) 所以,你的解释似乎不对:(不过,无论如何,感谢您提出使用tapply 的另一个选项。
    【解决方案2】:

    来自 Lightbend 的 Johannes 在这里回答了这个问题:https://groups.google.com/forum/#!topic/akka-user/NmQvcrz5sJg 答案是:

    您发现了磁铁图案的原因之一。如果你使用 requireParam("param".as[Int]) { abc => ... } 然后是 { abc => } block 被误认为是requireParam 的隐含参数。所以, 要么你同意,要么要求用户使用额外的括号 ((requireParam("param".as[Int])) { abc => ... }) 或者您使用 这个级别的磁铁图案也是如此。您可以阅读有关磁铁的信息 旧喷雾博客上的图案 (http://spray.io/blog/2012-12-13-the-magnet-pattern/) 或者看看 进入 akka-http 源代码。

    实现该功能的更好方法是使用 现有的实现;)并安装一个自定义拒绝处理程序 产生你想要的任何输出。看 https://doc.akka.io/docs/akka-http/10.0.10/scala/http/routing-dsl/rejections.html#customizing-rejection-handling 如何做到这一点。

    【讨论】:

      猜你喜欢
      • 2021-10-29
      • 1970-01-01
      • 1970-01-01
      • 2019-11-06
      • 1970-01-01
      • 1970-01-01
      • 2016-03-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多