Могу ли я создать директиву метода 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"
        }
      }
    }
  }

Ресурс: https://github.com/giftig/mediaman/blob/22b95a807f6e7bb64d695583f4b856588c223fc1/src/main/scala/com/programmingcentre/utils/utils/CorsSupport.scala

Я сделал это так:

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") {
    ...
  }
}
Другие вопросы по тегам