parameters

This page explains how to extract multiple query parameter values from the request, or parameters that might or might not be present.

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 UnmarshallerUnmarshaller in scope for that to work (see also Unmarshalling)
"amount".as(deserializer)
extract value of parameter “amount” with an explicit UnmarshallerUnmarshaller "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 UnmarshallerUnmarshaller in scope for that to work (see also Unmarshalling)
"distance".as(deserializer).*
extract multiple occurrences of parameter “distance” with an explicit UnmarshallerUnmarshaller

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

In order to filter on the existence of several query parameters, you need to nest as many parameter directives as desired.

Query parameters can be either extracted as a String or can be converted to another type. Different methods must be used when the desired parameter is required, optional or repeated.

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

If an unmarshaller throws an exception while extracting the value of a parameter, the request will be rejected with a MissingQueryParameterRejection if the unmarshaller threw an Unmarshaller.NoContentException or a MalformedQueryParamRejectionMalformedQueryParamRejection in all other cases. (see also Rejections)

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.

See When to use which parameter directive? to understand when to use which directive.

Examples

Required parameter

Scala
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'"
}
Java
final Route route = parameter("color", color ->
  parameter("backgroundColor", backgroundColor ->
    complete("The color is '" + color
               + "' and the background is '" + backgroundColor + "'")
  )
);

// tests:
testRoute(route).run(HttpRequest.GET("/?color=blue&backgroundColor=red"))
  .assertEntity("The color is 'blue' and the background is 'red'");

testRoute(route).run(HttpRequest.GET("/?color=blue"))
  .assertStatusCode(StatusCodes.NOT_FOUND)
  .assertEntity("Request is missing required query parameter 'backgroundColor'");

Optional parameter

Scala
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>'"
}
Java
final Route route = parameter("color", color ->
        parameterOptional("backgroundColor", backgroundColor ->
                complete("The color is '" + color
                        + "' and the background is '" + backgroundColor.orElse("undefined") + "'")
        )
);

// tests:
testRoute(route).run(HttpRequest.GET("/?color=blue&backgroundColor=red"))
        .assertEntity("The color is 'blue' and the background is 'red'");

testRoute(route).run(HttpRequest.GET("/?color=blue"))
        .assertEntity("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

Scala
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"
}
Java
final Route route = parameter("color", color ->
        parameter(StringUnmarshallers.INTEGER,"count", count ->
                complete("The color is '" + color + "' and you have " + count + " of it.")
        )
);
// tests:
testRoute(route).run(HttpRequest.GET("/?color=blue&count=42"))
        .assertEntity("The color is 'blue' and you have 42 of it.");

testRoute(route).run(HttpRequest.GET("/?color=blue&count=blub"))
        .assertStatusCode(StatusCodes.BAD_REQUEST)
        .assertEntity("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.