喷涂REST路由 - 过于冗长

时间:2015-10-19 10:45:54

标签: scala spray spray-json

有人能提供一些关于如何在喷涂中构建路由的好指示吗? 我的路线非常冗长,甚至IDEA在编辑包含路由的文件时变得非常慢(自动完成时间为5-10秒)....

      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
                  }
                }
              }
            }
          }
        }

3 个答案:

答案 0 :(得分:1)

您可以将路由拆分为多个路由。在您的情况下:客户,组,用户可能被提取到相应的路线。

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

然后你把它们结合起来:

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

答案 1 :(得分:1)

您可以将资源拆分为单独的特征,例如UserRoutes.scalaContentRoutes.scalaAdminRoutes.scala,并将它们全部扩展为HttpService。您现在可以拥有一个新类,它构成所有路由的组合,并扩展HttpServiceActor复合类可以使用~运算符链接您的分割路由。例如。 val routes = userRoutes.routes ~ adminRoutes.routes ~ contentRoutes.routes。它还为您提供了一个注入依赖项的好地方。

答案 2 :(得分: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)
          }
        }
      }
    }
  }      
} ~