【问题标题】:How to structure a RESTful API with Spray.io?如何使用 Spray.io 构建 RESTful API?
【发布时间】:2014-05-15 21:32:23
【问题描述】:

当我使用 Spray.io 开发 RESTful API 时,我应该如何构建我的应用程序?

我已经看到this answer 介绍了如何拆分 Spray 应用程序,但我对此并不满意,因为它似乎没有使用“每个请求一个参与者”的方法。我可以根据路径将来自根 Actor 的请求转发到我的应用程序中的其他 Actor,并在这些 Actor 内部定义相关路由吗?

谢谢

【问题讨论】:

    标签: scala rest spray spray-dsl


    【解决方案1】:

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

    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。您选择使用它的任何特殊原因?
    • CDI 是指依赖注入吗?
    • 是的。 /* Stackoverflow 需要更多字符,所以... */
    • 仅仅因为依赖注入一般来说是好的。它允许您注入模拟对象以独立对层进行单元测试,并让您在需要时交换层。
    • 看起来不错。
    【解决方案2】:

    我在创建第一个完整的 REST 项目时遇到了很多困难。我发现的示例是在 hello world 级别...我读过一些博客,一些 cmets,我决定创建示例项目。它基于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)
                        }
                    }
                }
            }
        }
    

    以及来自创建参与者处理程序的示例:

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

    【讨论】:

      猜你喜欢
      • 2011-06-08
      • 2017-11-15
      • 1970-01-01
      • 2015-05-14
      • 1970-01-01
      • 2015-06-13
      • 1970-01-01
      相关资源
      最近更新 更多