Могу ли я создать директиву метода OPTIONS по умолчанию для всех точек входа на моем маршруте?

Я не хочу явно писать:

options { ... }

для каждой точки входа/пути в моем маршруте Spray. Я хотел бы написать общий код, который добавит поддержку OPTIONS для всех путей. Он должен просматривать маршруты и извлекать из них поддерживаемые методы.

Я не могу вставить какой-либо код, так как не знаю, как к нему подойти в Spray.

Причина, по которой я это делаю, заключается в том, что я хочу предоставить самообнаруживаемый API, который придерживается принципов HATEOAS.


person user3816466    schedule 21.08.2014    source источник


Ответы (3)


Приведенная ниже директива сможет перехватить отклоненный запрос, проверить, является ли он опционным запросом, и вернуть:

  • Заголовки CORS для поддержки CORS (эта директива снимает ВСЮ защиту от cors, будьте осторожны!!!!!)
  • Заголовки Allow, чтобы предоставить узлу список доступных методов.

Попытайтесь понять приведенный ниже фрагмент и при необходимости откорректировать его. Вы должны предпочесть предоставить как можно больше информации, но если вы хотите вернуть только разрешенные методы, я предлагаю вам вырезать все остальное :).

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

person RoyB    schedule 28.11.2016

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

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*.

person sap1ens    schedule 01.09.2014
comment
Если добавили минус - объясните пожалуйста. Это решение отлично работает в продакшене для многих проектов. - person sap1ens; 12.08.2015
comment
Вы не ответили на заданный вопрос, он просит ответ, содержащий ключевое слово РАЗРЕШИТЬ. Это будет более полезным ответом, поскольку одноранговый узел будет знать, какие методы HTTP будут доступны. Кроме того, если вы хотите поддерживать CORS, вам необходимо предоставить заголовки CORS с ответом. - person RoyB; 28.11.2016
comment
@RoyB в вопросе нет ни слова о заголовках CORS или Access*. Ни одного. Но, конечно, мы можем предположить, что они необходимы, поэтому я обновил свой ответ. - person sap1ens; 30.11.2016
comment
На самом деле нет CORS, но разрешены заголовки для отображения того, какие методы разрешены. Я цитирую: It should look at the routes and extract supported methods from them. Предоставленное вами решение возвращает все существующие методы, а не методы, которые специально существуют для текущей конечной точки, поэтому ваше решение по-прежнему не рекомендуется и заставит браузеры использовать методы на конечных точках, которые им обычно не разрешено использовать. хит или те, которые могут даже не существовать. - person RoyB; 30.11.2016

Мне кажется, options достаточно общий, вы можете использовать его как:

path("foo") {
  options {
    ...
  }
} ~
path("bar") {
  options {
    ...
  }
}

или как это:

options {
  path("foo") {
    ...
  } ~
  path("bar") {
    ...
  }
}
person vitalii    schedule 21.08.2014
comment
Он компилируется так: options { get { ... } } но работает не так, как ожидалось - доступен только метод OPTIONS и я не могу вызвать внутренний маршрут с GET. У вас работает, может я что-то упускаю? - person user3816466; 21.08.2014
comment
Я имею в виду, что стараюсь не писать path("foo") { options {...} ~ get {...} }. - person user3816466; 21.08.2014
comment
да, он работает для меня в любой комбинации. Вы можете создать пользовательскую директиву в спрее, см. spray. io/documentation/1.1-SNAPSHOT/spray-routing/, но это немного сложно, так как сигнатуры типов могут быть довольно сложными. Кстати, возможно, вы хотите что-то вроде этого: val getOrPut = get | помещать ? - person vitalii; 26.08.2014