繁体   English   中英

使用自定义requireParam()指令的Akka-HTTP编译错误

[英]Akka-HTTP compilation error using custom requireParam() directive

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

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。

由于“指令”伴随对象的apply方法而发生此错误。 IT允许通过带有参数(T => Route)=> 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参数必须是一个元组。 在您的情况下,编译器无法构建Route。 您的requireParam(“ param” .as [Int])返回Directive1 [Int],因此由于Int不是元组,所以apply方法无效。

为了使这项工作有效,您应该直接使用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]

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

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

您发现了磁铁图案的原因之一。 如果使用requireParam("param".as[Int]) { abc => ... }{ abc => }块会被误认为是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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM