Akka HTTP 在路由 dsl 中很好地支持提取路径查询参数(那些在 ? 之后,由 & 连接的参数),但不支持由 ; 分隔的路径参数。(如 /my/path;JSESSIONID=123)
这如何最好地完成?
比我想象的要容易。可能会消除对 scalaz (Lens) 的依赖并稍微优化代码,但现在就可以了。
顺便说一句,还发现路径参数破坏了将路径与路径指令匹配的能力:/my/path;JSESSIONID=123 与指令路径不匹配(“my”/“path”)
下面的解决方案通过从请求上下文中删除路径参数来解决这个问题,而是提供它们。
Akka 人注意:也许你可以在框架中加入类似的东西,这样下一个从路径参数中获取 JSESSIONID 的人不必实现相同的?
def pathParams: Directive1[List[String]] = {
val prv = provide(List.empty[String])
def somePathParams(ctxPathLens: Lens[RequestContext, Path]) =
extract(ctx => (Slash ~ Segments).apply(ctxPathLens.get(ctx))).flatMap {
case Matched(_, Tuple1(path)) =>
path.takeRight(1) match {
case last :: Nil => last.split(';').toList match {
case lastHead :: lastTail => provide(lastTail) & mapRequestContext(
ctxPathLens.set(_, Path((path.dropRight(1) :+ lastHead).mkString("/", "/", ""))))
case _ => prv
}
case _ => prv
}
case _ => prv
}
val unmatchedPath = somePathParams(Lens.lensu((ctx, path) =>
ctx.mapUnmatchedPath(_ => path),
_.unmatchedPath))
val requestPath = somePathParams(Lens.lensu((ctx, path) =>
ctx.mapRequest(r => r.withUri(r.uri.withPath(path)))
, _.request.uri.path))
unmatchedPath.tflatMap(_ => Directive.Empty) & requestPath
}
def pathParamsMap: Directive1[Map[String, String]] =
pathParams.map(_.map(_.split('=').toList match {
case key :: Nil => key -> ""
case key :: values => key -> values.mkString("=")
case _ => ???
}).toMap)
def optionalPathParam(name: String): Directive1[Option[String]] =
pathParamsMap.map(_.get(name))
def optionalPathParamSessionId:Directive1[Option[UUID]] =
optionalPathParam(jsessionidKey).map(_.flatMap(j => Try(UUID.fromString(j)).toOption))