喷涂:如何将respondWithHeaders应用于所有路由而不是每个路由

时间:2014-04-11 08:39:59

标签: scala http-headers httpresponse spray

我正在学习Scala,他还不熟悉使用Spray框架构建一些REST-API应用程序并面临这个问题:我的所有HTTP响应都应该有特定的标头(Access-Control-Allow-Origin)。因此,我无法了解如何将其设置为所有应用程序的响应一次,而不是每个。

我的路线如下:

trait Statistics extends HttpService { self: StatisticModuleLike =>

  implicit def MM: MarshallerM[Future]

  lazy val statisticsRoute =
    path("statistics" / "applications" / Segment / Segment ) { (measure, period) =>
      get {
        respondWithHeader(RawHeader("Access-Control-Allow-Origin", "*")) {
          complete {
            getAppCount(MeasureType.withName(measure), period.toInt)
          }
        }
      }
    } ~
    path("statistics" / "approvals" / Segment / Segment ) { (measure, period) =>
      get {
        respondWithHeader(RawHeader("Access-Control-Allow-Origin", "*")) {
          complete {
            getApproval(MeasureType.withName(measure), period.toInt)
          }
        }
      }
    } ~
      path("statistics" / "amounts" / Segment / Segment ) { (measure, period) =>
        get {
          respondWithHeader(RawHeader("Access-Control-Allow-Origin", "*")) {
            complete {
              getAmount(MeasureType.withName(measure), period.toInt)
            }
          }
        }
      } ~
      path("statistics" / "sellers" / "snooze") {
        get {
          respondWithHeader(RawHeader("Access-Control-Allow-Origin", "*")) {
            complete {
              getSellerSnooze(MeasureType.withName("Month"), 100)
            }
          }
        }
      } ~
      path("statistics" / "sellers" / "snooze" / Segment / Segment ) { (measure, period) =>
        get {
          respondWithHeader(RawHeader("Access-Control-Allow-Origin", "*")) {
            complete {
                getSellerSnooze(MeasureType.withName(measure), period.toInt)
            }
          }
        }
      } ~
      path("statistics" / "sellers" / "growing" / Segment / Segment ) { (measure, period) =>
        get {
          parameter('percent.as[Int] ? 0) { percent =>
            respondWithHeader(RawHeader("Access-Control-Allow-Origin", "*")) {
              complete {
                getSellerDynamic(MeasureType.withName(measure), period.toInt, DynamicTrendType.withName("Growing"), percent)
              }
            }
          }
        }
      } ~
      path("statistics" / "sellers" / "falling" / Segment / Segment ) { (measure, period) =>
        get {
          parameters('percent.as[Int] ? 0, 'average.as[Int] ? 0) { (percent, average) =>
            respondWithHeader(RawHeader("Access-Control-Allow-Origin", "*")) {
              complete {
                getSellerDynamic(MeasureType.withName(measure), period.toInt, DynamicTrendType.withName("Falling"), percent)
              }
            }
          }
        }
      }
}

如您所见,添加

respondWithHeader(RawHeader("Access-Control-Allow-Origin", "*"))

每条路都不方便......

有什么可爱的解决方法吗?比方说,例如,使用一些自定义扩展HttpService并使用它而不是基础一个?

2 个答案:

答案 0 :(得分:4)

这是一条很大的路线=)。实际上,spact指令是完全可组合的,因此不需要复制,你可以将结构简化为:

respondWithHeader(RawHeader("Access-Control-Allow-Origin", "*")) {
  (pathPrefix("statistics") & get) {
    pathPrefix("applications") { 
      path(Segment / Segment) { (measure, period) =>
        complete { getAppCount(MeasureType.withName(measure), period.toInt) }
      }
    } ~
    pathPrefix("applications") { ... } ~
    path("amounts") { ... } ~
    ... 
  }  
}

其中PathPrefix检查路径以给定前缀开头,Path简单匹配路由的其余部分,还有pathSuffix,pathEnd等...

同样为了简化大型结构,我发现使用Cake Pattern组合Spray并制作所谓的Handler来处理你的逻辑很有用,这个解决方案更灵活,更容易测试:

trait SprayRoute extends CounterHandler with ... {
  val service: Route = {
    respondWithHeader(RawHeader("Access-Control-Allow-Origin", "*")) {
      (pathPrefix("statistics") & get) {
        pathPrefix("applications") { 
          path(Segment / Segment) { CounterHandler }
        } ~
        pathPrefix("applications") { ... } ~
        path("amounts") { ... } ~
        ... 
      }  
    }
  }
}

trait CounterHandler {
  def CounterHandler: (String, String) => Route = { (measure, period) =>
    complete { getAppCount(MeasureType.withName(measure), period.toInt) }
  }
}

答案 1 :(得分:3)

您只需将您的主路径包裹在respondWithHeader指令周围即可。参见示例(Spray 1.1。:

object HelloRouting extends SimpleRoutingApp with App{
  implicit val system = ActorSystem("test")
  import system.dispatcher
  startServer(interface= "localhost", port= 8082){
    lazy val api = pathPrefix("api"){
        path("hello"){
          get{
            complete( "Hello, World" )
          }
        }
    }
    respondWithHeader(RawHeader("X-My-Header", "My Header is awesome!")) {
        api
    }
  }
}