我学习的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,并使用它而不是基本服务?
这是一条很大的路线=)。实际上,喷雾指令是完全可组合的,所以不需要这种复制,你可以将你的结构简化为这样的:
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和制作所谓的Handlers很有用,它可以处理您的逻辑,这个解决方案更灵活,更容易测试:
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) }
}
}
您只需围绕responsdWithHeader指令包装主路由即可。参见示例(喷雾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
}
}
}