我正在使用Scala和Akka Http编写REST API。
对于请求处理,我以RequestContext => Future[RouteResult]
在每个路由中,我必须将用户的上下文从函数传递给函数,这变得有点麻烦。例如,在下面的代码中,我总是必须将userContext
传递给与数据库交互的每个函数。
val route: Route = {
requestContext => {
val userContext = extractUser(requestContext)
val computeResult = compute(userContext)
requestContext.complete(computeResult)
}
}
但是,如果我将上下文设置为单例,那么当下一次调用进入时它会冒被覆盖的风险,因为API是多租户。
有没有更好的方法来解决这个问题?
答案 0 :(得分:0)
给予读者Monad一个机会。这将允许您对UserContext进行更好的抽象。
这可能是一个aprox:
import scala.concurrent.Future
import scala.util.{Failure, Success}
import scalaz.{Reader, ReaderT}
trait UserContext {
val user: String
val passwd: String
}
trait YourDBFunctionsII[T] {
def compute(): Reader[UserContext, T]
def computeII(): Reader[UserContext, T]
}
object YourDBFunctionsII extends YourDBFunctionsII[String] {
override def compute(): Reader[UserContext, String] = Reader {
in: UserContext =>
???
}
override def computeII(): Reader[UserContext, String] = Reader {
in: UserContext =>
???
}
}
class YourRoutesII {
import YourDBFunctionsII._
val route: Route = { requestContext =>
{
val userContext: UserContext = ??? // Extract from RequestContext
val routines = for {
resul1 <- compute()
resul2 <- computeII()
} yield resul2
// Execute monad composition
val computeResult = routines.run(userContext)
requestContext.complete(computeResult)
}
}
}
如果需要处理异步数据库驱动程序,可以使用ScalaZ ReaderT类型:
trait YourDBFunctionsIII[T] {
def compute(): ReaderT[Future, UserContext, T]
def computeII(): ReaderT[Future, UserContext, T]
}
// In case you want to deal with Futures
object YourDBFunctionsIII extends YourDBFunctionsIII[String] {
override def compute(): ReaderT[Future, UserContext, String] = ReaderT {
ctx =>
Future {
// Do something
ctx.passwd
}.recover {
case e: Throwable =>
"Error"
}
}
override def computeII(): ReaderT[Future, UserContext, String] = ReaderT {
ctx =>
Future {
// Do other thing
ctx.passwd
}
}
}
class YourRoutesIII {
import YourDBFunctionsIII._
val route: Route = { requestContext =>
{
val userContext: UserContext = ??? // Extract from RequestContext
val routines = for {
resul1 <- compute()
resul2 <- computeII()
} yield resul2
// Execute monad composition
val computeResult = routines.run(userContext)
requestContext.complete(computeResult)
}
}
}
在这两种情况下,您只需要使用UserContext实例运行monad合成。