我开发了自定义通用指令,它将提供给定类型的参数(如果存在),或者使用我的自定义异常拒绝。
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。
答案 0 :(得分:1)
由于Directive伴随对象应用方法而发生此错误。 IT允许从具有参数(T => 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],因此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]
也许有一个更好的解决方案,并避免tapply
答案 1 :(得分:0)
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 如何做到这一点。