我想让Spray路由中的参数解析不区分大小写。例如:
val route: Route = {
(path("search") & get) {
parameters('pagesize.as[Int] ?, 'appId ?) { (pageSize, appId) =>
...
}
}
}
在这条路线中,我希望 pageSize 和 appId 参数不区分大小写。例如,pagesize=5 或 PAGESIZE=5。
我想让Spray路由中的参数解析不区分大小写。例如:
val route: Route = {
(path("search") & get) {
parameters('pagesize.as[Int] ?, 'appId ?) { (pageSize, appId) =>
...
}
}
}
在这条路线中,我希望 pageSize 和 appId 参数不区分大小写。例如,pagesize=5 或 PAGESIZE=5。
我将 Mustafa 的回答更进一步并扩展了参数语法,因此您可以将不区分大小写的参数与区分大小写的参数一起使用(类型类和磁铁模式的万岁大吉!):
package caseinsensitive
import spray.routing.SimpleRoutingApp
import akka.actor.ActorSystem
import spray.routing.directives.BasicDirectives
import spray.routing.directives.RouteDirectives
import scala.language.implicitConversions
/**/
import spray.httpx.unmarshalling.{ FromStringOptionDeserializer ⇒ FSOD, _}
trait ToCINameReceptaclePimps {
implicit def symbol2CINR(symbol: Symbol) = new CINameReceptacleHelper[String](symbol.name)
implicit def string2CINR(string: String) = new CINameReceptacleHelper[String](string)
}
case class CINameReceptacleHelper[T](name: String) {
def insensitive = CINameReceptacle[T](name)
}
case class CINameReceptacle[A](name: String) {
def as[B] = CINameReceptacle[B](name)
def as[B](deserializer: FSOD[B]) = CINameDeserializerReceptacle(name, deserializer)
def ? = as[Option[A]]
def ?[B](default: B) =CINameDefaultReceptacle(name, default)
def ![B](requiredValue: B) = CIRequiredValueReceptacle(name, requiredValue)
}
case class CINameDeserializerReceptacle[A](name: String, deserializer: FSOD[A]) {
def ? = CINameDeserializerReceptacle(name, Deserializer.liftToTargetOption(deserializer))
def ?(default: A) = CINameDeserializerDefaultReceptacle(name, deserializer, default)
def !(requiredValue: A) = CIRequiredValueDeserializerReceptacle(name, deserializer, requiredValue)
}
case class CINameDefaultReceptacle[A](name: String, default: A)
case class CIRequiredValueReceptacle[A](name: String, requiredValue: A)
case class CINameDeserializerDefaultReceptacle[A](name: String, deserializer: FSOD[A], default: A)
case class CIRequiredValueDeserializerReceptacle[A](name: String, deserializer: FSOD[A], requiredValue: A)
/**/
trait CaseInsensitiveParams extends ToCINameReceptaclePimps {
import spray.routing._
import spray.routing.directives._
import spray.httpx.unmarshalling.{ FromStringOptionDeserializer ⇒ FSOD, _ }
import BasicDirectives._
import RouteDirectives._
type ParamDefMagnetAux[A, B] = ParamDefMagnet2[A] { type Out = B }
def ParamDefMagnetAux[A, B](f: A ⇒ B) = new ParamDefMagnet2[A] { type Out = B; def apply(value: A) = f(value) }
private def extractParameter[A, B](f: A ⇒ Directive1[B]) = ParamDefMagnetAux[A, Directive1[B]](f)
private def filterCI[T](paramName: String, fsod: FSOD[T]): Directive1[T] =
extract(ctx ⇒ fsod(ctx.request.uri.query.find(_._1.equalsIgnoreCase(paramName)).map(_._2))).flatMap {
case Right(x) ⇒ provide(x)
case Left(ContentExpected) ⇒ reject(MissingQueryParamRejection(paramName))
case Left(MalformedContent(error, cause)) ⇒ reject(MalformedQueryParamRejection(paramName, error, cause))
case Left(x: UnsupportedContentType) ⇒ throw new IllegalStateException(x.toString)
}
/************ "regular" parameter extraction ******************/
implicit def forCINDesR[T] = extractParameter[CINameDeserializerReceptacle[T], T] { nr ⇒
filterCI(nr.name, nr.deserializer)
}
implicit def forCINDefR[T](implicit fsod: FSOD[T]) = extractParameter[CINameDefaultReceptacle[T], T] { nr ⇒
filterCI(nr.name, fsod.withDefaultValue(nr.default))
}
implicit def forCINDesDefR[T] = extractParameter[CINameDeserializerDefaultReceptacle[T], T] { nr ⇒
filterCI(nr.name, nr.deserializer.withDefaultValue(nr.default))
}
implicit def forCINR[T](implicit fsod: FSOD[T]) = extractParameter[CINameReceptacle[T], T] { nr ⇒
filterCI(nr.name, fsod)
}
/************ required parameter support ******************/
private def requiredFilterCI(paramName: String, fsod: FSOD[_], requiredValue: Any): Directive0 =
extract(ctx ⇒ fsod(ctx.request.uri.query.find(_._1.equalsIgnoreCase(paramName)).map(_._2))).flatMap {
case Right(value) if value == requiredValue ⇒ pass
case _ ⇒ reject
}
implicit def forCIRVR[T](implicit fsod: FSOD[T]) = ParamDefMagnetAux[CIRequiredValueReceptacle[T], Directive0] { rvr ⇒
requiredFilterCI(rvr.name, fsod, rvr.requiredValue)
}
implicit def forCIRVDR[T] = ParamDefMagnetAux[CIRequiredValueDeserializerReceptacle[T], Directive0] { rvr ⇒
requiredFilterCI(rvr.name, rvr.deserializer, rvr.requiredValue)
}
}
object Main extends App with SimpleRoutingApp with CaseInsensitiveParams {
implicit val system = ActorSystem("my-system")
startServer(interface = "localhost", port = 8080) {
path("hello") {
parameters("foo".insensitive.?) { foo =>
get {
complete {
<h1>You said {foo} </h1>
}
}
}
}
}
}
如果您不需要所有这些,则可以使用parameterMap
指令并从那里获取参数。
看起来从 URI 逻辑获取查询参数是硬编码在ParamDefMagnet2.filter函数中的。
为了克服这个限制,我会复制该代码,替换逻辑ctx.request.uri.query.find(_._1.equalsIgnoreCase(paramName))
并在需要时导入它。
示例用法如下所示:
import CaseInsensitiveQueryParameters._
val route: Route = {
(path("search") & get) {
parameters('pagesize.as[Int] ?, 'appId ?) { (pageSize, appId) =>
...
}
}
}
并且更改的隐式如下所示:
object CaseInsensitiveQueryParameters {
type ParamDefMagnetAux[A, B] = ParamDefMagnet2[A] { type Out = B }
def ParamDefMagnetAux[A, B](f: A ⇒ B) = new ParamDefMagnet2[A] { type Out = B; def apply(value: A) = f(value) }
import spray.httpx.unmarshalling.{ FromStringOptionDeserializer ⇒ FSOD, _ }
import BasicDirectives._
import RouteDirectives._
/************ "regular" parameter extraction ******************/
private def extractParameter[A, B](f: A ⇒ Directive1[B]) = ParamDefMagnetAux[A, Directive1[B]](f)
private def filter[T](paramName: String, fsod: FSOD[T]): Directive1[T] =
extract(ctx ⇒ fsod(ctx.request.uri.query.find(_._1.equalsIgnoreCase(paramName)))).flatMap {
case Right(x) ⇒ provide(x)
case Left(ContentExpected) ⇒ reject(MissingQueryParamRejection(paramName))
case Left(MalformedContent(error, cause)) ⇒ reject(MalformedQueryParamRejection(paramName, error, cause))
case Left(x: UnsupportedContentType) ⇒ throw new IllegalStateException(x.toString)
}
implicit def forString(implicit fsod: FSOD[String]) = extractParameter[String, String] { string ⇒
filter(string, fsod)
}
implicit def forSymbol(implicit fsod: FSOD[String]) = extractParameter[Symbol, String] { symbol ⇒
filter(symbol.name, fsod)
}
implicit def forNDesR[T] = extractParameter[NameDeserializerReceptacle[T], T] { nr ⇒
filter(nr.name, nr.deserializer)
}
implicit def forNDefR[T](implicit fsod: FSOD[T]) = extractParameter[NameDefaultReceptacle[T], T] { nr ⇒
filter(nr.name, fsod.withDefaultValue(nr.default))
}
implicit def forNDesDefR[T] = extractParameter[NameDeserializerDefaultReceptacle[T], T] { nr ⇒
filter(nr.name, nr.deserializer.withDefaultValue(nr.default))
}
implicit def forNR[T](implicit fsod: FSOD[T]) = extractParameter[NameReceptacle[T], T] { nr ⇒
filter(nr.name, fsod)
}
}