1

我想让Spray路由中的参数解析不区分大小写。例如:

val route: Route = {
  (path("search") & get) {
    parameters('pagesize.as[Int] ?, 'appId ?) { (pageSize, appId) => 
      ...
    }
  }
}

在这条路线中,我希望 pageSize 和 appId 参数不区分大小写。例如,pagesize=5 或 PAGESIZE=5。

4

2 回答 2

2

我将 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指令并从那里获取参数。

于 2016-02-26T17:02:08.343 回答
1

看起来从 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)
  }
}
于 2016-02-26T09:32:46.533 回答