我正在学习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并使用它而不是基础一个?
答案 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
}
}
}