Как структурировать RESTful API с помощью Spray.io?
Когда я использую Spray.io для разработки RESTful API, как мне структурировать свое приложение?
Я уже видел этот ответ о том, как разделить приложение Spray, но меня это не устраивает, поскольку в нем, похоже, не используется подход "один субъект на запрос". Могу ли я пересылать запросы от корневого субъекта другим субъектам в моем приложении на основе путей и определять внутри этих участников соответствующие маршруты?
Спасибо
2 ответа
Вы, конечно, можете пересылать запросы от одного актера другому, основываясь на путях или чем-то еще. Посмотрите мой пример проекта (который является форком от проекта примера):
Повторно добавьте код от основного субъекта, который получает все запросы и направляет их другим субъектам, которые обрабатывают каждый сервис:
def receive = runRoute {
compressResponseIfRequested(){
alwaysCache(simpleCache) {
pathPrefix("movies") { ctx => asb.moviesRoute ! ctx } ~
pathPrefix("people") { ctx => asb.peopleRoute ! ctx }
} ~
pathPrefix("login") { ctx => asb.loginRoute ! ctx } ~
pathPrefix("account") { ctx => asb.accountRoute ! ctx }
}
}
И, например, маршрут фильма:
def receive = runRoute {
get {
parameters('query, 'page ? 1).as(TitleSearchQuery) { query =>
val titleSearchResults = ms.getTitleSearchResults(query)
complete(titleSearchResults)
}~
path(LongNumber) { movieId =>
val movie = ms.getMovie(movieId)
complete(movie)
}~
path(LongNumber / "cast") { movieId =>
val movieCast = ms.getMovieCast(movieId)
complete(movieCast)
}~
path(LongNumber / "trailers") { movieId =>
val trailers = ms.getTrailers(movieId)
complete(trailers)
}
}
}
Я много боролся с созданием первого полноценного проекта REST. Примеры, которые я нашел, были на привет мировом уровне... Я прочитал несколько блогов, немного комментариев, и я решил создать пример проекта. Он основан на скала / акка / спрей / mysql
Это полный рабочий пример с websocket для уведомления клиентов об изменении данных и т. Д. Вы можете проверить это на https://github.com/vixxx123/scalasprayslickexample
Вот пример кода маршрутизации из этого проекта:
val personCreateHandler = actorRefFactory.actorOf(RoundRobinPool(2).props(Props[CreateActor]), s"${TableName}CreateRouter")
val personPutHandler = actorRefFactory.actorOf(RoundRobinPool(5).props(Props[UpdateActor]), s"${TableName}PutRouter")
val personGetHandler = actorRefFactory.actorOf(RoundRobinPool(20).props(Props[GetActor]), s"${TableName}GetRouter")
val personDeleteHandler = actorRefFactory.actorOf(RoundRobinPool(2).props(Props[DeleteActor]), s"${TableName}DeleteRouter")
val userRoute =
pathPrefix("person") {
pathEnd {
get {
ctx => personGetHandler ! GetMessage(ctx, None)
} ~
post {
entity(as[Person]) {
entity =>
ctx => personCreateHandler ! CreateMessage(ctx, entity)
}
}
} ~
pathPrefix (IntNumber){
entityId => {
pathEnd {
get {
ctx => personGetHandler ! GetMessage(ctx, Some(entityId))
} ~ put {
entity(as[Person]) { entity =>
ctx => personPutHandler ! PutMessage(ctx, entity.copy(id = Some(entityId)))
}
} ~ delete {
ctx => personDeleteHandler ! DeleteMessage(ctx, entityId)
} ~ patch {
ctx => personPutHandler ! PatchMessage(ctx, entityId)
}
}
}
}
}
И пример из обработчика создания актера:
override def receive: Receive = {
case CreateMessage(ctx, person) =>
val localCtx = ctx
connectionPool withSession {
implicit session =>
try {
val resId = PersonsIdReturning += person
val addedPerson = person.copy(id = Some(resId.asInstanceOf[Int]))
localCtx.complete(addedPerson)
publishAll(CreatePublishMessage(TableName, localCtx.request.uri + "/" + addedPerson.id.get, addedPerson))
L.debug(s"Person create success")
} catch {
case e: Exception =>
L.error(s"Ups cannot create person: ${e.getMessage}", e)
localCtx.complete(e)
}
}
}
По-прежнему отсутствуют две важные вещи: oauth2 и push-уведомления для конкретного пользователя / соединения через websocket.