Могу ли я создать директиву метода OPTIONS по умолчанию для всех точек входа в мой маршрут?
Я не хочу явно писать:
options { ... }
для каждой точки входа / пути в моем маршруте Spray. Я хотел бы написать некоторый общий код, который добавит OPTIONS
поддержка всех путей. Следует посмотреть на маршруты и извлечь из них поддерживаемые методы.
Я не могу вставить любой код, так как я не знаю, как подойти к нему в Spray.
Причина, по которой я это делаю, заключается в том, что я хочу предоставить самораскрываемый API, который придерживается принципов HATEOAS.
3 ответа
Приведенная ниже директива сможет перехватить отклоненный запрос, проверить, является ли это запросом опции, и вернуть:
- Заголовки CORS, для поддержки CORS (эта директива снимает ВСЕ защиту cors, будьте осторожны!!!!!)
- Разрешить заголовки, чтобы дать пиру список доступных методов
Попытайтесь понять приведенный ниже фрагмент и отрегулируйте его, где это необходимо. Вы должны предпочесть предоставить как можно больше информации, но если вы хотите вернуть только Разрешенные методы, я предлагаю вам исключить остальные:).
import spray.http.{AllOrigins, HttpMethods, HttpMethod, HttpResponse}
import spray.http.HttpHeaders._
import spray.http.HttpMethods._
import spray.routing._
/**
* A mixin to provide support for providing CORS headers as appropriate
*/
trait CorsSupport {
this: HttpService =>
private val allowOriginHeader = `Access-Control-Allow-Origin`(AllOrigins)
private val optionsCorsHeaders = List(
`Access-Control-Allow-Headers`(
"Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, " +
"Referer, User-Agent"
),
`Access-Control-Max-Age`(60 * 60 * 24 * 20) // cache pre-flight response for 20 days
)
def cors[T]: Directive0 = mapRequestContext {
context => context.withRouteResponseHandling {
// If an OPTIONS request was rejected as 405, complete the request by responding with the
// defined CORS details and the allowed options grabbed from the rejection
case Rejected(reasons) if (
context.request.method == HttpMethods.OPTIONS &&
reasons.exists(_.isInstanceOf[MethodRejection])
) => {
val allowedMethods = reasons.collect { case r: MethodRejection => r.supported }
context.complete(HttpResponse().withHeaders(
`Access-Control-Allow-Methods`(OPTIONS, allowedMethods :_*) ::
allowOriginHeader ::
optionsCorsHeaders
))
}
} withHttpResponseHeadersMapped { headers => allowOriginHeader :: headers }
}
}
Используйте это так:
val routes: Route =
cors {
path("hello") {
get {
complete {
"GET"
}
} ~
put {
complete {
"PUT"
}
}
}
}
Я сделал это так:
private val CORSHeaders = List(
`Access-Control-Allow-Methods`(GET, POST, PUT, DELETE, OPTIONS),
`Access-Control-Allow-Headers`("Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, Referer, User-Agent"),
`Access-Control-Allow-Credentials`(true)
)
def respondWithCORS(origin: String)(routes: => Route) = {
val originHeader = `Access-Control-Allow-Origin`(SomeOrigins(Seq(HttpOrigin(origin))))
respondWithHeaders(originHeader :: CORSHeaders) {
routes ~ options { complete(StatusCodes.OK) }
}
}
val routes =
respondWithCORS(config.getString("origin.domain")) {
pathPrefix("api") {
// ... your routes here
}
}
Таким образом, каждый запрос OPTION к любому URL с префиксом /api возвращает 200 кодов.
Обновление: добавлены заголовки Access*.
Мне кажется options
достаточно универсален, вы можете использовать его как:
path("foo") {
options {
...
}
} ~
path("bar") {
options {
...
}
}
или как это:
options {
path("foo") {
...
} ~
path("bar") {
...
}
}