RB_*_*RB_ 8 architecture scala routes akka-http
我正在编写基于Akka-HTTP的REST API.由于我是Akka和Scala的新手,我不确定在我的项目中组织代码的最佳方法是什么.我会有约.7个具有基本CRUD的不同实体.这意味着我将在API中拥有超过25条路线.我想保持路由根据它们逻辑关联的实体进行分组.有什么可能是实现这个目标的好方法?目前,我从GitHub上的一些项目中获取灵感,并将这些路线分为特征.我有一个主要特征,包括一些一般的东西,扩展指令和添加编组:
trait Resource extends Directives with JsonSupport{
...
}
Run Code Online (Sandbox Code Playgroud)
然后我有其他组织如下所示.
trait UserResource extends Resource{
def userRoutes:Route =
pathPrefix("authenticate") {
pathEndOrSingleSlash {
post {
entity(as[LoginRequest]) { request =>
...
}
}
}
}
} ~
pathPrefix("subscribe") {
pathEndOrSingleSlash {
post {
entity(as[UserSubscribeRequest]) { request =>
...
}
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
有一个类定义异常处理程序,实例化一些帮助程序并将路由放在一起:
class Routes extends UserResource with OtherResource with SomeOtherResource{
... handlers definitions ...
... helpers ...
def allRoutesUnified: Route =
handleErrors {
cors() {
pathPrefix("api") {
authenticateOAuth2("api", PasswordAuthenticator) { _ =>
//Routes defined in other traits
otherRoutes ~ someOtherRoutes
}
} ~ userRoutes
}
}
}
Run Code Online (Sandbox Code Playgroud)
最后在app入口点:
object Main extends App{
... usual Akka stuff ..
val routes = new Routes ()
val router = routes.allRoutesUnified
Http().bindAndHandle(router, "localhost", 8080)
}
Run Code Online (Sandbox Code Playgroud)
什么可以是更好或更优雅的组织路线的方式?
问题中的编码组织和结构更类似于面向对象编程而不是函数式编程.功能是否优于OO超出了stackoverflow的范围,但可能我们选择了scala而不是原因.
此外,在特定示例中,即使您要使用OO路由,也似乎不需要继承.继承唯一实现的是避免单个import语句.
功能组织
更实用的方法是在对象内部而不是类中指定更简单的路径.此外,Route您创建的值不需要实例化,def因为您可以为了不同的目的重复使用相同的Route:
import akka.http.scaladsl.server.Directives._
//an object, not a class
object UserResource {
//Note: val not def
val userRoutes : Route = {
//same as in question
}
}
Run Code Online (Sandbox Code Playgroud)
使用对象仍然允许您在一个统一结构下将类似的路由组合在一起,但不需要实例化一个类对象,只是为了能够访问路由.
同样,你的定义allRoutesUnified应该是一个更高阶的函数,它将"内部逻辑"作为一个参数.这将有助于更好地组织代码并使单元测试更容易:
object Routes {
import UserResources.userRoutes
def allRoutesUnified(innerRoute : Directive0 = userRoutes) : Route =
handleErrors {
cors() {
pathPrefix {
authenticateOAuth2("api", PasswordAuthenticator) { _ =>
innerRoute
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在这个函数可以用于除了之外的路由userRoutes.
最后,可以以类似于问题的方式访问统一的高阶函数,但不需要Routes使用new以下方法创建对象:
object Main extends App {
//no need to create a routes objects, i.e. no "new Routes()"
Http().bindAndHandle(
Routes.allRoutesUnified(),
"localhost",
8080
)
}
Run Code Online (Sandbox Code Playgroud)