如何使用Spray.io构建RESTful API?

9
当我使用Spray.io开发RESTful API时,应该如何构建我的应用程序?
我已经看过这个答案关于如何分离Spray应用程序的这个答案,但是我不太满意它,因为它似乎没有使用“每个请求一个actor”的方法。我能否根据路径将请求从根actor转发到应用程序中的其他actor,并在这些actor中定义相关路由?
谢谢
2个回答

8

您可以根据路径或其他方式将请求从一个Actor转发到另一个Actor。请查看我的示例项目(这是一个示例项目的分支的分支):

https://github.com/gangstead/spray-moviedb/blob/master/src/main/scala/com/example/routes/ApiRouter.scala

以下是主要执行者的相关代码,它接收所有请求并将其路由到处理每个服务的其他执行者:

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

我在这里看了你的示例,并注意到你使用了CDI。为什么你选择使用它呢? - Carlos Melo
你所说的CDI是指依赖注入吗? - Gangstead
是的。/* Stackoverflow要求更多字符,所以... */ - Carlos Melo
2
仅仅因为依赖注入在一般情况下是有益的。它允许您注入模拟对象以独立地对层进行单元测试,并且如果需要,可以轻松替换层。 - Gangstead
看起来是一个不错的模式。 - bonez

0

我在创建第一个完整的REST项目时遇到了很多困难。我找到的示例只是hello world级别的......我读了几篇博客,几条评论,然后决定创建一个示例项目。它基于scala/akka/spray/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)
                    }
                }
            }
        }
    }

创建Actor处理程序的示例:

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和通过websocket向特定用户/连接推送通知


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接