Спрей: как применить responseWithHeaders ко всем маршрутам, а не к каждому

Я изучаю 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 с некоторой настройкой и использовать его вместо базового?


person Alexander    schedule 11.04.2014    source источник
comment
Конечно вы можете. Вам не нравится решение с расширением HttpService и его настройкой?   -  person maks    schedule 11.04.2014
comment
@maks Немного не уверен в расширении HttpService: функция, которая мне нужна, - это только настройка ответа, а не весь HttpService ... Поэтому я предлагаю, должен быть более короткий и удобный для клиента способ избежать расширения базового компонента только для некоторых основных функций параметризация.   -  person Alexander    schedule 11.04.2014


Ответы (2)


Это большой маршрут =). На самом деле директивы спрея прекрасно компонуются, поэтому нет необходимости в этом дублировании, вы можете сократить свою структуру до чего-то вроде этого:

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 simple совпадает с остальной частью маршрута, также есть pathSuffix, pathEnd и т. д.

Кроме того, чтобы упростить большие структуры, я счел полезным составить Spray с шаблоном торта и создать так называемые обработчики, которые будут обрабатывать вашу логику, это решение более гибкое и его гораздо проще тестировать:

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) }
  }
}
person 4lex1v    schedule 12.04.2014
comment
Сначала мне было не совсем понятно, что эти директивы вполне компонуемые и гибкие. Спасибо! - person Alexander; 16.04.2014

Вы просто можете обернуть свой основной маршрут вокруг директивы responseWithHeader. См. пример (Распыление 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
    }
  }
}
person Guillaume Belrose    schedule 11.04.2014
comment
К сожалению, не могу заставить его работать... (используя Spray 1.2) - person Alexander; 11.04.2014
comment
Спасибо! Вы меня вдохновили на такую ​​вещь: val route = { responseWithHeader(RawHeader(Access-Control-Allow-Origin, *)) {usersRoute ~sellersRoute ~statisticRoute} } - person Alexander; 11.04.2014