2015-10-19 43 views
2

任何人都可以提供一些关于如何在喷雾中构建路由的好方法吗? 我的路线编辑包含路由文件....喷射REST路由 - 过于冗长

 pathPrefix("customers") { 
     pathEnd { 
      get { 
      handleRejections(RejectionHandler.apply(handleMissingAuthSessionKey orElse handleValidationErrorAsUnauthorized)) { 
       withSessionKey[String]("groups") { g => 
       validate(g.contains("admin"), "Not authorized") { 
        complete { 
        m.getCustomers 
        } 
       } 
       } 
      } 
      } 
     } ~ 
     path(IntNumber) { id => 
      post { 
      handleRejections(RejectionHandler.apply(handleMissingAuthSessionKey orElse handleValidationErrorAsUnauthorized)) { 
       withSessionKey[String]("groups") { g => 
       validate(g.contains("admin"), "Not authorized") { 
        entity(as[Customer]) { c => 
        complete { 
         m.updateCustomer(c).map { 
         case 0 => StatusCodes.UnprocessableEntity 
         case 1 => StatusCodes.Accepted 
         case _ => StatusCodes.InternalServerError 
         }.handleSuccessWith { case _ => 
         siblingWorkers ! Push("customers", None) 
         } 
        } 
        } 
       } 
       } 
      } 
      } ~ 
      delete { 
      withSessionKey[String]("groups") { g => 
       validate(g.contains("admin"), "Not authorized") { 
       complete { 
        m.deleteCustomer(id).map { 
        case 0 => StatusCodes.UnprocessableEntity 
        case 1 => StatusCodes.Accepted 
        case _ => StatusCodes.InternalServerError 
        }.handleSuccessWith { case _ => 
        siblingWorkers ! Push("customers", None) 
        } 
       } 
       } 
      } 
      } 
     } ~ 
     path("new") { 
      handleRejections(RejectionHandler.apply(handleMissingAuthSessionKey orElse handleValidationErrorAsUnauthorized)) { 
      post { 
       withSessionKey[String]("groups") { g => 
       validate(g.contains("admin"), "Not authorized") { 
        entity(as[Customer]) { c => 
        complete { 
         m.insertCustomer(c).handleSuccessWith { case _ => 
         siblingWorkers ! Push("customers", None) 
         } 
        } 
        } 
       } 
       } 
      } 
      } 
     } 
     } ~ 
     pathPrefix("groups") { 
     pathEnd { 
      get { 
      handleRejections(RejectionHandler.apply(handleMissingAuthSessionKey orElse handleValidationErrorAsUnauthorized)) { 
       withSessionKey[String]("groups") { g => 
       validate(g.contains("admin"), "Not authorized") { 
        complete { 
        m.getGroups 
        } 
       } 
       } 
      } 
      } 
     } ~ 
     pathPrefix(IntNumber) { groupId => 
      pathEnd { 
      complete { 
       m.getGroupById(groupId) 
      } 
      } ~ 
      path("users") { 
      complete { 
       m.getGroupById(groupId).flatMap { groupO => 
       groupO.map { group => 
        m.getUsers(group) 
       }.getOrElse(Future.successful(Seq())) 
       } 
      } 
      } 
     } ~ 
     pathPrefix(Segment) { groupName => 
      pathEnd { 
      complete { 
       m.getGroupByName(groupName) 
      } 
      } ~ 
      path("users") { 
      complete { 
       m.getGroupByName(groupName).flatMap { groupO => 
       groupO.map { group => 
        m.getUsers(group) 
       }.getOrElse(Future.successful(Seq())) 
       } 
      } 
      } 
     } 
     } ~ 
     pathPrefix("users") { 
     path("me") { 
      handleRejections(RejectionHandler.apply(handleMissingAuthSessionKey orElse handleValidationErrorAsUnauthorized)) { 
      withSessionKey[String]("userId") { uid => 
       complete { 
       m.getUserById(uid.toInt).map(_.get) 
       } 
      } 
      } 
     } ~ 
     path("new") { 
      handleRejections(RejectionHandler.apply(handleMissingAuthSessionKey orElse handleValidationErrorAsUnauthorized orElse RejectionHandler.Default)) { 
      withSessionKey[String]("groups") { g => 
       validate(g.contains("admin"), "Not authorized") { 
       entity(as[NewUser]) { r => 
        complete { 
        m.addUser(r).handleCompletionWith{ _ => siblingWorkers ! Push("users", None)} 
        } 
       } 
       } 
      } 
      } 
     } ~ 
     pathPrefix(IntNumber) { uid => 
      pathEnd { 
      get { 
       handleRejections(RejectionHandler.apply(handleMissingAuthSessionKey orElse handleValidationErrorAsUnauthorized orElse RejectionHandler.Default)) { 
       withSessionKey[String]("groups") { g => 
        validate(g.contains("admin"), "Not authorized") { 
        complete { 
         m.getUserById(uid) 
        } 
        } 
       } 
       } 
      } ~ 
      post { 
       handleRejections(RejectionHandler.apply(handleMissingAuthSessionKey orElse handleValidationErrorAsUnauthorized orElse RejectionHandler.Default)) { 
       withSessionKey[String]("groups") { g => 
        validate(g.contains("admin"), "Not authorized") { 
        entity(as[User]) { u => 
         complete { 
         m.updateUser(u).map { 
          case 0 => StatusCodes.UnprocessableEntity 
          case 1 => StatusCodes.Accepted 
          case _ => StatusCodes.InternalServerError 
         }.handleCompletionWith { _ => siblingWorkers ! Push("users", None) } 
         } 
        } 
        } 
       } 
       } 
      } ~ 
      delete { 
       handleRejections(RejectionHandler.apply(handleMissingAuthSessionKey orElse handleValidationErrorAsUnauthorized orElse RejectionHandler.Default)) { 
       withSessionKey[String]("groups") { g => 
        validate(g.contains("admin"), "Not authorized") { 
        complete { 
         m.deleteUserById(uid).map { 
         case 0 => StatusCodes.UnprocessableEntity 
         case 1 => StatusCodes.Accepted 
         case _ => StatusCodes.InternalServerError 
         }.handleCompletionWith{ _ => siblingWorkers ! Push("groups", None)} 
        } 
        } 
       } 
       } 
      } 
      } ~ 
      pathPrefix("groups") { 
      path("new") { 
       entity(as[NewUserGroup]) { g => 
       complete { 
        m.addUserGroup(UserGroup(uid, g.id)).map { 
        case 0 => StatusCodes.UnprocessableEntity 
        case 1 => StatusCodes.Accepted 
        case _ => StatusCodes.InternalServerError 
        }.handleCompletionWith{ _ => siblingWorkers ! Push("groups", None)} 
       } 
       } 
      } ~ 
      pathEnd { 
       get { 
       complete { 
        m.getUserGroups(uid) 
       } 
       } ~ 
       post { 
       entity(as[Seq[Int]]) { groups => 
        complete { 
        m.setUserGroups(uid, groups).map { 
         case Some(x) if x < groups.length => StatusCodes.UnprocessableEntity 
         case Some(x) if x == groups.length => StatusCodes.OK 
         case _ => StatusCodes.InternalServerError 
        }.handleCompletionWith{ _ => siblingWorkers ! Push("users", None)} 
        } 
       } 
       } 
      } 
      } 
     } ~ 
     pathEnd { 
      get { 
      handleRejections(RejectionHandler.apply(handleMissingAuthSessionKey orElse handleValidationErrorAsUnauthorized)) { 
       withSessionKey[String]("groups") { g => 
       validate(g.contains("admin"), "Not authorized") { 
        complete { 
        m.getUsers 
        } 
       } 
       } 
      } 
      } 
     } 

回答

1

您可以将路由分成几个路由时,得到了非常详细,甚至IDEA变得非常慢(5-10秒自动完成)。在你的情况下:客户,组,用户可能被提取到相应的路线。

class CustomersRoute extends ... { 
    def route: Route = pathPrefix("customers") { 
    ... 
    } 
} 

然后你将它们结合起来:

val routes = pathPrefix("v1") { 
    customers.route ~ 
    groups.route ~ 
    users.route 
} 
+0

将如何与已提取的参数(上面的隐式会话)这项工作? –

+0

您可以在每条路由的构造函数中声明隐式参数 – Nyavro

+0

这是否意味着每次请求都会重新评估路由?与在启动时进行评估相反,只是在每次请求时都运行? –

1

您可以将资源划分成分离的特征,例如UserRoutes.scalaContentRoutes.scalaAdminRoutes.scala,并让他们都继承HttpService。您现在可以拥有一个新班级,它可以形成所有路线的组合并延伸到HttpServiceActor复合班级可以使用~运算符链接您的拆分路线。例如。 val routes = userRoutes.routes ~ adminRoutes.routes ~ contentRoutes.routes。它也为您提供了一个注入依赖关系的好地方。

+0

这将如何与已提取的参数一起工作? (在上面的例子中隐含的会话) –

1

除了其他人已经提到的内容之外,我还会利用编写指令的能力和创建自定义指令来减少重复代码并使路由结构更具可读性。该公司经常重复的代码

有一件事情是这样的:

handleRejections(RejectionHandler.apply(handleMissingAuthSessionKey orElse handleValidationErrorAsUnauthorized orElse RejectionHandler.Default)) { 
    withSessionKey[String]("groups") { g => 
    validate(g.contains("admin"), "Not authorized") { 
    … 
    } 
    } 
} 

你可能会考虑保理说出来到自定义指令。像这样(未经):

def handleSessionKeyValidation(key: String, requiredValue: String) = { 
    val rejectionHandlers = handleRejections(RejectionHandler.apply(handleMissingAuthSessionKey orElse handleValidationErrorAsUnauthorized)) 
    val sessionKeyDir = withSessionKey[String](key) 
    (rejectionHandlers & sessionKeyDir).flatMap[HNil](value => 
     if (value.contains(requiredValue)) pass 
     else reject(AuthorizationFailedRejection) 
    ) 
    } 

现在开始结合您的HTTP方法和路径的指令,还可以使用自定义指令,然后选择路线的第一部分可能看起来更像是这样的:

(get & path("customers") & pathEnd) { 
    handleSessionKeyValidation("groups", "admin"){ 
    complete{ 
     m.getCustomers 
    } 
    } 
} ~ 
(post & path("customers"/IntNumber)) { id => 
    handleSessionKeyValidation("groups", "admin"){ 
    entity(as[Customer]) { c => 
     complete { 
     m.updateCustomer(c).map { 
      case 0 => StatusCodes.UnprocessableEntity 
      case 1 => StatusCodes.Accepted 
      case _ => StatusCodes.InternalServerError 
     }.handleSuccessWith { case _ => 
      siblingWorkers ! Push("customers", None) 
     } 
     } 
    } 
    } 
} ~ 
(delete & path("customers"/IntNumber)) { id => 
    handleSessionKeyValidation("groups", "admin") { 
    complete { 
     m.deleteCustomer(id).map { 
     case 0 => StatusCodes.UnprocessableEntity 
     case 1 => StatusCodes.Accepted 
     case _ => StatusCodes.InternalServerError 
     }.handleSuccessWith { case _ => 
     siblingWorkers ! Push("customers", None) 
     } 
    } 
    } 
} ~ 
(post & path("customers"/"new")) { 
    handleSessionKeyValidation("groups", "admin"){ 
    entity(as[Customer]) { c => 
     complete { 
     m.insertCustomer(c).handleSuccessWith { case _ => 
      siblingWorkers ! Push("customers", None) 
     } 
     } 
    } 
    } 
} 

即使在上面的例子中,handleSessionKeyValidation指令的用法有点重复。我们可以通过增加handleSessionKeyValidation的范围和包装更大部分的路线来减少重复。像这样的东西。

pathPrefixTest("customers"){ 
    handleSessionKeyValidation("groups", "admin") { 
    (get & path("customers") & pathEnd) { 
     complete{ 
     m.getCustomers 
     } 
    } ~ 
    (post & path("customers"/IntNumber)) { id => 
     entity(as[Customer]) { c => 
     complete { 
      m.updateCustomer(c).map { 
      case 0 => StatusCodes.UnprocessableEntity 
      case 1 => StatusCodes.Accepted 
      case _ => StatusCodes.InternalServerError 
      }.handleSuccessWith { case _ => 
      siblingWorkers ! Push("customers", None) 
      } 
     } 
     } 
    } ~ 
    (delete & path("customers"/IntNumber)) { id => 
     complete { 
     m.deleteCustomer(id).map { 
      case 0 => StatusCodes.UnprocessableEntity 
      case 1 => StatusCodes.Accepted 
      case _ => StatusCodes.InternalServerError 
     }.handleSuccessWith { case _ => 
      siblingWorkers ! Push("customers", None) 
     } 
     } 
    } ~ 
    (post & path("customers"/"new")) { 
     entity(as[Customer]) { c => 
     complete { 
      m.insertCustomer(c).handleSuccessWith { case _ => 
      siblingWorkers ! Push("customers", None) 
      } 
     } 
     } 
    } 
    }  
} ~ 
+0

除了这个答案,spray'runRoute'函数还会收到一个隐式的'RejectionHandler',所以你可以将它从'handleSessionKeyValidation'函数移到其他地方。 –