2015-10-19 78 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'函數移到其他地方。 –