我想了解Spray中的指令是如何工作的。 As per the documentation:
指令的一般解剖如下:
name(arguments) { extractions => ... // inner Route }
我的基本理解是,在下面的代码段中,32
作为参数传递给方法test
。
test {
32
}
但是,在上面的指令name
示例中,它被称为参数传递到内部路由,这是一个匿名函数。
有人可以帮我理解语法和流程,从如何提取参数并传递到内部路径开始?
答案 0 :(得分:8)
您的语法正确地将32
传递给函数test
。你缺少的是Directive
接受函数作为参数(记住,我们现在正在进行函数式编程,因此函数是值)。如果你想写这个:
path(IntNumber) {
userId =>
complete(s"Hello user $userId")
}
以较少DSL-ey的方式,你可以这样做:
val innerFunction: Int => Route = {userId => complete(s"Hello user $userId")}
(path(IntNumber))(innerFunction)
甚至是这样:
def innerMethod(userId: Int): Route = complete(s"Hello user $userId")
(path(IntNumber))(innerMethod)
实际完成这项工作的机制是......复杂;此方法使Directive
可隐式转换为函数:
implicit def pimpApply[L <: HList](directive: Directive[L])(implicit hac: ApplyConverter[L]): hac.In ⇒ Route = f ⇒ directive.happly(hac(f))
这是使用“磁体模式”来选择一个合适的hac
,这样如果指令提取参数或者一个值,它就可以在内部路径中使用一个函数(具有适当数量的参数)如果指令不提取参数,则为内部路径(普通路径)。代码看起来比它复杂,因为scala没有直接支持完全依赖类型,所以我们必须通过implicits来模拟它。有关这个必要的可怕代码,请参阅ApplyConverterInstances
:/。
当我们在特定指令的happly
方法中获得实际路线时,实际提取全部发生。 (如果到处都使用了HList
,我们大多可以避免/忽略前面的恐怖事件)。大多数extract-ey指令(例如path
)最终会调用hextract
:
def hextract[L <: HList](f: RequestContext ⇒ L): Directive[L] = new Directive[L] {
def happly(inner: L ⇒ Route) = ctx ⇒ inner(f(ctx))(ctx)
}
请记住,Route
实际上只是RequestContext => Unit
,因此当传递Route
时会返回RequestContext
:
f
,以提取需要提取的内容(例如网址路径组件)inner
; inner
是例如ApplyConverter
的函数。路径组件到内部路径。(以下是通过评论对话的mod编辑的):
从根本上说它非常优雅,你可以看到所有的喷码和普通的scala代码(我真的建议你在困惑时阅读源代码)。但是{{1}}的“桥接”部分是复杂的,而且实际上没有办法解决这个问题。它试图用一种并非真正为它们设计的语言来完全依赖类型。
你必须记住,喷涂路由DSL是DSL;这是几乎任何其他语言中你必须拥有的外部配置文件。我想不出一个单一的Web框架在路由定义方面提供了相同的灵活性,喷涂可以实现完整的编译时类型安全性。所以是的,喷涂的一些东西很复杂 - 但正如引用的那样,容易的东西应该是容易的,而且应该是可能的。所有scala级别的东西都很简单;喷雾是复杂的,但在另一种语言中会更复杂(不可思议)。