Анализ параметров без учета регистра в маршрутизации спрея

Я хотел бы сделать синтаксический анализ параметров в маршрутизации Spray нечувствительным к регистру. Например:

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

В этом маршруте я хотел бы, чтобы параметры pageSize и appId работали без учета регистра. Например, pagesize=5 ИЛИ PAGESIZE=5.


person Rahul Singhai    schedule 25.02.2016    source источник
comment
вы можете создать пользовательскую директиву для использования вместо параметров. Я сейчас не за компьютером, но посмотреть на реализацию параметров не составит труда.   -  person Gabriele Petronella    schedule 25.02.2016


Ответы (2)


Я сделал ответ Мустафы еще на один шаг и расширил синтаксис параметров, поэтому вы можете использовать параметры, не чувствительные к регистру, вместе с чувствительными к регистру (большое ура для классов типов и шаблона магнита!):

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 и получить оттуда параметры.

person vitalii    schedule 26.02.2016

Похоже, что получение параметров запроса из логики 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)
  }
}
person Mustafa Simav    schedule 26.02.2016