parameters

Signature

def parameters(param: <ParamDef[T]>): Directive1[T]
def parameters(params: <ParamDef[T_i]>*): Directive[T_0 :: ... T_i ... :: HNil]
def parameters(params: <ParamDef[T_0]> :: ... <ParamDef[T_i]> ... :: HNil): Directive[T_0 :: ... T_i ... :: HNil]

The signature shown is simplified and written in pseudo-syntax, the real signature uses magnets. [1] The type <ParamDef> doesn’t really exist but consists of the syntactic variants as shown in the description and the examples.

[1] See The Magnet Pattern for an explanation of magnet-based overloading.

Description

The parameters directive filters on the existence of several query parameters and extract their values.

Query parameters can be either extracted as a String or can be converted to another type. The parameter name can be supplied either as a String or as a Symbol. Parameter extraction can be modified to mark a query parameter as required, optional, or repeated, or to filter requests where a parameter has a certain value:

"color"
extract value of parameter “color” as String
"color".?
extract optional value of parameter “color” as Option[String]
"color" ? "red"
extract optional value of parameter “color” as String with default value "red"
"color" ! "blue"
require value of parameter “color” to be "blue" and extract nothing
"amount".as[Int]
extract value of parameter “amount” as Int, you need a matching Deserializer in scope for that to work (see also Unmarshalling)
"amount".as(deserializer)
extract value of parameter “amount” with an explicit Deserializer
"distance".*
extract multiple occurrences of parameter “distance” as Iterable[String]
"distance".as[Int].*
extract multiple occurrences of parameter “distance” as Iterable[Int], you need a matching Deserializer in scope for that to work (see also Unmarshalling)
"distance".as(deserializer).*
extract multiple occurrences of parameter “distance” with an explicit Deserializer

You can use Case Class Extraction to group several extracted values together into a case-class instance.

Requests missing a required parameter or parameter value will be rejected with an appropriate rejection.

There’s also a singular version, parameter. Form fields can be handled in a similar way, see formFields. If you want unified handling for both query parameters and form fields, see anyParams.

Examples

Required parameter

val route =
  parameters('color, 'backgroundColor) { (color, backgroundColor) =>
    complete(s"The color is '$color' and the background is '$backgroundColor'")
  }

// tests:
Get("/?color=blue&backgroundColor=red") ~> route ~> check {
  responseAs[String] shouldEqual "The color is 'blue' and the background is 'red'"
}
Get("/?color=blue") ~> Route.seal(route) ~> check {
  status shouldEqual StatusCodes.NotFound
  responseAs[String] shouldEqual "Request is missing required query parameter 'backgroundColor'"
}

Optional parameter

val route =
  parameters('color, 'backgroundColor.?) { (color, backgroundColor) =>
    val backgroundStr = backgroundColor.getOrElse("<undefined>")
    complete(s"The color is '$color' and the background is '$backgroundStr'")
  }

// tests:
Get("/?color=blue&backgroundColor=red") ~> route ~> check {
  responseAs[String] shouldEqual "The color is 'blue' and the background is 'red'"
}
Get("/?color=blue") ~> route ~> check {
  responseAs[String] shouldEqual "The color is 'blue' and the background is '<undefined>'"
}

Optional parameter with default value

val route =
  parameters('color, 'backgroundColor ? "white") { (color, backgroundColor) =>
    complete(s"The color is '$color' and the background is '$backgroundColor'")
  }

// tests:
Get("/?color=blue&backgroundColor=red") ~> route ~> check {
  responseAs[String] shouldEqual "The color is 'blue' and the background is 'red'"
}
Get("/?color=blue") ~> route ~> check {
  responseAs[String] shouldEqual "The color is 'blue' and the background is 'white'"
}

Parameter with required value

val route =
  parameters('color, 'action ! "true") { (color) =>
    complete(s"The color is '$color'.")
  }

// tests:
Get("/?color=blue&action=true") ~> route ~> check {
  responseAs[String] shouldEqual "The color is 'blue'."
}

Get("/?color=blue&action=false") ~> Route.seal(route) ~> check {
  status shouldEqual StatusCodes.NotFound
  responseAs[String] shouldEqual "The requested resource could not be found."
}

Deserialized parameter

val route =
  parameters('color, 'count.as[Int]) { (color, count) =>
    complete(s"The color is '$color' and you have $count of it.")
  }

// tests:
Get("/?color=blue&count=42") ~> route ~> check {
  responseAs[String] shouldEqual "The color is 'blue' and you have 42 of it."
}

Get("/?color=blue&count=blub") ~> Route.seal(route) ~> check {
  status shouldEqual StatusCodes.BadRequest
  responseAs[String] shouldEqual "The query parameter 'count' was malformed:\n'blub' is not a valid 32-bit signed integer value"
}

Repeated parameter

val route =
  parameters('color, 'city.*) { (color, cities) =>
    cities.toList match {
      case Nil         => complete(s"The color is '$color' and there are no cities.")
      case city :: Nil => complete(s"The color is '$color' and the city is $city.")
      case multiple    => complete(s"The color is '$color' and the cities are ${multiple.mkString(", ")}.")
    }
  }

// tests:
Get("/?color=blue") ~> route ~> check {
  responseAs[String] === "The color is 'blue' and there are no cities."
}

Get("/?color=blue&city=Chicago") ~> Route.seal(route) ~> check {
  responseAs[String] === "The color is 'blue' and the city is Chicago."
}

Get("/?color=blue&city=Chicago&city=Boston") ~> Route.seal(route) ~> check {
  responseAs[String] === "The color is 'blue' and the cities are Chicago, Boston."
}

CSV parameter

val route =
  parameter("names".as(CsvSeq[String])) { names =>
    complete(s"The parameters are ${names.mkString(", ")}")
  }

// tests:
Get("/?names=") ~> route ~> check {
  responseAs[String] shouldEqual "The parameters are "
}
Get("/?names=Caplin") ~> route ~> check {
  responseAs[String] shouldEqual "The parameters are Caplin"
}
Get("/?names=Caplin,John") ~> route ~> check {
  responseAs[String] shouldEqual "The parameters are Caplin, John"
}

Repeated, deserialized parameter

val route =
  parameters('color, 'distance.as[Int].*) { (color, distances) =>
    distances.toList match {
      case Nil             => complete(s"The color is '$color' and there are no distances.")
      case distance :: Nil => complete(s"The color is '$color' and the distance is $distance.")
      case multiple        => complete(s"The color is '$color' and the distances are ${multiple.mkString(", ")}.")
    }
  }

// tests:
Get("/?color=blue") ~> route ~> check {
  responseAs[String] === "The color is 'blue' and there are no distances."
}

Get("/?color=blue&distance=5") ~> Route.seal(route) ~> check {
  responseAs[String] === "The color is 'blue' and the distance is 5."
}

Get("/?color=blue&distance=5&distance=14") ~> Route.seal(route) ~> check {
  responseAs[String] === "The color is 'blue' and the distances are 5, 14."
}
The source code for this page can be found here.