5

I have (OData) query params defined in my route like so:

parameters(('$top.as[Int].?, '$skip.as[Int].?)) { (top, skip) =>

I have the following rejection handler to handle all invalid parameters (handleAll):

RejectionHandler.newBuilder()
  .handleAll[MalformedQueryParamRejection] { paramRejections =>
    // paramRejections is a Seq[MalformedQueryParamRejection]
    ...
  }

The problem is that when called with the following

some-endpoint?$top=invalid&$skip=invalid

The paramRejections in the rejection handler has 2 entries, both for $top, instead of one for $top and one for $skip.

Seems related to the dollar sign on the params, since when I remove this things work as expected. Is this a known issue or is there a workaround available (that doesn't include removing the dollar sign)?

Note, it seems its only the rejection handler that has a problem with multiple params starting with the dollar sign, since this line in the route correctly assigns top and skip to the variables when $top and $skip are supplied with valid values in the URI:

parameters(('$top.as[Int].?, '$skip.as[Int].?)) { (top, skip) =>
Rory
  • 798
  • 2
  • 12
  • 37
  • Does percent-encoding help with this issue? `%24top=` instead of `$top=`. Dollar sign is reserved as sub-delim and parsed properly only if percent encoded. It worked ok in my reproduction. – Dennis Tsoi Oct 23 '17 at 07:59
  • No, clients should be allowed to use dollar sign, as per OData standards: http://www.odata.org/documentation/odata-version-2-0/uri-conventions/ – Rory Oct 23 '17 at 09:08
  • @chunjef thats irrelevant, all that matters is that the paramRejections supplied to handleAll is incorrect, as described in my question – Rory Oct 24 '17 at 08:34
  • Possible to get a minimum git repo for debugging? – Tarun Lalwani Oct 24 '17 at 12:54

1 Answers1

1

There is an issue with your route tree configuration, probably two candidate routes are evaluated and each one produce a MalformedQueryParamRejection for the $top query parameter.

The paramRejections in the rejection handler has 2 entries, both for $top, instead of one for $top and one for $skip.

handleAll does not collect multiple MalformedQueryParamRejection originated from the same route, but it collects rejections from different routes.

paramRejections is a Seq[MalformedQueryParamRejection] but a single route could be rejected with just one MalformedQueryParamRejection, specifically (only) the first query parameter not matching the required format.

Try it with a minimal route configuration (as in the example below) and you'll experience the right behaviour:

val route = get {
  parameters(('$top.as[Int].?, '$skip.as[Int].?)) {
    (top, skip) => complete(s"$top, $skip")
  }
}
Federico Pellegatta
  • 3,977
  • 1
  • 17
  • 29
  • You are correct, there is an open issue to implement the behaviour I require; https://github.com/akka/akka-http/issues/1490 – Rory Oct 30 '17 at 09:25