问题描述
我想创建一个将包装一些Routes的自定义指令,然后该内部指令将允许内部路由访问UserContext对象(如果存在)。
authenticated { uc =>
post {
// ... can use uc object here
}
}
case class UserContext(username: String)
def authenticated: Directive1[Option[UserContext]] =
for {
credentials <- extractCredentials
result <- {
credentials match {
case Some(c) if c.scheme.equalsIgnoreCase("Bearer") => UserContext(c.token()) // authenticate(c.token)
case _ => None //rejectUnauthenticated(AuthenticationFailedRejection.CredentialsMissing)
}
}
} yield result
错误是:
value map is not a member of UserRoutes.this.UserContext
[error] credentials match {
[error] ^
[error] one error found
[error] (Compile / compileIncremental) Compilation Failed
我正在将VS Code与bloop一起使用,并且鼠标悬停错误显示:
值映射不是具有java.io.Serializable的产品的成员
extractCredentials返回一个Option [HttpCredentials],所以我不确定为什么不能匹配它或映射它。
解决方法
您在这里做什么:
case Some(c) if c.scheme.equalsIgnoreCase("Bearer") => UserContext(c.token()) // authenticate(c.token)
case _ => None //rejectUnauthenticated(AuthenticationFailedRejection.CredentialsMissing)
返回UserContext
或None
。一些编译器必须推断这两者之间的通用类型。每个case class
或case object
实现Product with Serializable
,因此它将是两个case
实例的LUB,它们没有实现其他公共共享超类型。
因此,将推断result
右侧的内容是Product with Serializable
而不是Option[UserContext]
。 for
的理解是,Option
带有map
,可以在其上调用.map { result => result }
,我们认为类型不匹配。
相信您想在这里
case Some(c) if c.scheme.equalsIgnoreCase("Bearer") => Some(UserContext(c.token()))
case _ => None
以防万一,提醒:Option
与Java中的@nullable
不同,因此a: A
不会自动提升为Some(a): Option[A]
,而None
是与null
不同。
编辑:
另一件事是,您拥有flatMap并在2种不同的类型(指令和选项)上进行映射,因此您不能仅将两者结合在一起进行理解。我认为您想做类似的事情:
extractCredentials.map { credentials =>
credentials.collect {
case c if c.scheme.equalsIgnoreCase("Bearer") => UserContext(c.token())
}
}
,
除了 Serializable产品的错误外,您还试图创建一个指令。如果使用Some(UserContext)修复代码,则下一个错误是:
Error:(113,14) type mismatch;
required: akka.http.scaladsl.server.Directive
编译器说,当您对指令执行flatMap时,您需要返回一个新的指令而不是一个Option。
此时,您可以从函数 (Tuple1(Option [用户] =>路由)=>路由 )创建指令。考虑到路由是 RequestContext => Future [RouteResult] ,例如:
def authenticated: Directive1[Option[UserContext]] = {
extractCredentials.flatMap { credentials =>
Directive { inner =>
ctx => {
val result = credentials match {
case Some(c) if c.scheme.equalsIgnoreCase("Bearer") => Some(UserContext(c.token())) // authenticate(c.token)
case _ => None //rejectUnauthenticated(AuthenticationFailedRejection.CredentialsMissing)
}
inner(Tuple1(result))(ctx)
}
}
}
}
有了这个,您就有了一个新指令 已认证 ,该指令使用extractCredentials。