Akka Http API单例用户上下文

时间:2017-09-27 20:24:30

标签: scala akka akka-http

我正在使用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是多租户。

有没有更好的方法来解决这个问题?

1 个答案:

答案 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合成。