To ensure people don't append random query parameters (e.g. appending &r=234522.123
or similar) to avoid hitting our cache I want to have a way to reject any queries that are not handled explicitly. I can of course create one that contains a whitelist, but that would have to be separately maintained and I hate maintaining two things that needs to stay in synch. (Though, it would aid in failing faster.) Is this possible with Spray routing?
Asked
Active
Viewed 277 times
2

4lex1v
- 21,367
- 6
- 52
- 86

Stig Brautaset
- 2,602
- 1
- 22
- 39
3 Answers
1
I ended up with this:
// This contains a white-list of allowed query parameters. This is useful to
// ensure people don't try to use &r=234234 to bust your caches.
def allowedParameters(params: String*): Directive0 = parameterSeq.flatMap {
case xs =>
val illegal = xs.collect {
case (k, _) if !params.contains(k) => k
}
if (illegal.nonEmpty)
reject(ValidationRejection("Illegal query parameters: " + illegal.mkString("", ", ", "\nAllowed ones are: ") + params.mkString(", ")))
else
pass
}
For usage, have a look at the unit tests:
val allowedRoute = {
allowedParameters("foo", "bar") {
complete("OK")
}
}
"Allowed Parameter Directive" should "reject parameters not in its whitelist" in {
Get("/?foo&bar&quux") ~> allowedRoute ~> check {
handled should equal(false)
rejection should be(ValidationRejection("Illegal query parameters: quux\nAllowed ones are: foo, bar"))
}
}
it should "allow properly sorted parameters through" in {
Get("/?bar&foo") ~> allowedRoute ~> check {
handled should equal(true)
responseAs[String] should equal("OK")
}
}

Stig Brautaset
- 2,602
- 1
- 22
- 39
1
Actually, you have a good solution, i can only suggest a small refactoring:
def only(params: String*): Directive0 = {
def check: Map[String, String] => Boolean = _.keySet diff params.toSet isEmpty
parameterMap.require(check, rejection)
}
You can write it as a one-liner, but it would be just longer

4lex1v
- 21,367
- 6
- 52
- 86
-
That is a nice refactor. Thanks! – Stig Brautaset Mar 12 '14 at 23:08
-2
With a route like this:
val myRoute = {
...
pathPrefix("test") {
parameter("good") {
good =>
complete("GOOD")
}
} ~
...
}
Spray will require first parameter to be good
and to have value, i.e. ?good=value
. No other parameters that have values are allowed.

yǝsʞǝla
- 16,272
- 2
- 44
- 65
-
I don't understand how you get to that conclusion. Do you mean that other parameters are *ignored*? That is not what I want. I want the route to be rejected with an error message. – Stig Brautaset Mar 06 '14 at 20:18
-
I meant that if there are any other query parameters that have value, i.e. `param=value` except for the `good=value` then the route will be rejected with an error message. `url/test?good=true` - accepted, `url/test?bad=true?good=true` - rejected, `url/test?good=true?bad=true` - rejected. However for some reason params without values are not rejected: `url/test?good=true?bad` or `url/test?bad?good=true` are accepted. – yǝsʞǝla Mar 06 '14 at 20:36
-
I think we talk about two different things when we say "rejected". Your `/test?good=...` route will only be *accepted* if you have a parameter called good, with a value, in the request. But it doesn't reject other parameters in the sense I want; I want don't want superfluous arguments to be merely ignored, I want them to be an error. – Stig Brautaset Mar 11 '14 at 22:44
-
Spray just converts all your params into map or seq of tuples, and with parameter directive extract from this collection data you want. It would reject `url/test?good=true?bad=true` cause it's an invalid url query – 4lex1v Mar 12 '14 at 17:49