简体   繁体   English

Spray路由中的不区分大小写的参数解析

[英]Case insensitive parameter parsing in Spray routing

I would like to make parameter parsing in Spray routing case insensitive. 我想在Spray路由不区分大小写中进行参数解析。 For example: 例如:

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

In this route, I would like the pageSize and appId parameters to work as case insensitive. 在这个路由中,我希望pageSize和appId参数作为不区分大小写的工作。 For example, pagesize=5 OR PAGESIZE=5. 例如,pagesize = 5 OR PAGESIZE = 5。

I took Mustafa's answer one step further and extended parameter syntax, so you can use non-case sensitive parameters together with case-sensitive (Big Hooray for typeclasses and magnet pattern!): 我将Mustafa的答案进一步扩展并扩展了参数语法,因此您可以将非区分大小写的参数与区分大小写的一起使用(对于类型类和磁体模式,大Hooray!):

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>
          }
        }
      }
    }
  }
}

If you don't need all of this than you can just use parameterMap directive and get parameters from there. 如果你不需要所有这些,你只需使用parameterMap指令并从那里获取参数。

Looks like getting query parameters from URI logic is hardcoded in ParamDefMagnet2.filter function. 看起来从URI逻辑获取查询参数是在ParamDefMagnet2.filter函数中硬编码的。

In order to overcome this limitation, I would duplicate that code, replace logic with ctx.request.uri.query.find(_._1.equalsIgnoreCase(paramName)) and import it when needed. 为了克服这个限制,我会复制该代码,用ctx.request.uri.query.find(_._1.equalsIgnoreCase(paramName))替换逻辑并在需要时导入它。

Example usage would look like this: 示例用法如下所示:

import CaseInsensitiveQueryParameters._

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

And changed implicits would look like this: 改变的含义看起来像这样:

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)
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM