htp #19678 add variadic concat route combinator
* add variadic route concatenation function * add `concat` to `~` warning bubble in docs * fix grammar of warning bubble, and clarify
This commit is contained in:
parent
09d5072f2c
commit
b3c85512a3
4 changed files with 51 additions and 4 deletions
|
|
@ -138,8 +138,8 @@ transformations, both (or either) on the request and on the response side.
|
||||||
Composing Directives
|
Composing Directives
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
.. note:: Gotcha: forgetting the ``~`` (tilde) character in between directives can often result in perfectly valid
|
.. note:: Gotcha: forgetting the ``~`` (tilde) character in between directives can result in perfectly valid
|
||||||
Scala code that compiles but lead to your composed directive only containing the up to where ``~`` is missing.
|
Scala code that compiles but does not work as expected. What would be intended as a single expression would actually be multiple expressions, and only the final one would be used as the result of the parent directive. Alternatively, you might choose to use the ``concat`` combinator. ``concat(a, b, c)`` is the same as ``a ~ b ~ c``.
|
||||||
|
|
||||||
As you have seen from the examples presented so far the "normal" way of composing directives is nesting.
|
As you have seen from the examples presented so far the "normal" way of composing directives is nesting.
|
||||||
Let's take a look at this concrete example:
|
Let's take a look at this concrete example:
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,6 @@ class StreamUtilsSpec extends AkkaSpec {
|
||||||
Await.ready(whenCompleted, 3.seconds).value shouldBe Some(Failure(ex))
|
Await.ready(whenCompleted, 3.seconds).value shouldBe Some(Failure(ex))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
"downstream cancels" in {
|
"downstream cancels" in {
|
||||||
val (newSource, whenCompleted) = StreamUtils.captureTermination(Source(List(1, 2, 3)))
|
val (newSource, whenCompleted) = StreamUtils.captureTermination(Source(List(1, 2, 3)))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,42 @@ class BasicRouteSpecs extends RoutingSpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"routes created by the 'sequence' directive" should {
|
||||||
|
"reject with zero arguments, since no routes were matched" in {
|
||||||
|
Get() ~> {
|
||||||
|
concat()
|
||||||
|
} ~> check { rejections shouldEqual Seq() }
|
||||||
|
}
|
||||||
|
"yield the first sub route if it succeeded" in {
|
||||||
|
Get() ~> {
|
||||||
|
concat(
|
||||||
|
get { complete("first") },
|
||||||
|
get { complete("second") })
|
||||||
|
} ~> check { responseAs[String] shouldEqual "first" }
|
||||||
|
}
|
||||||
|
"yield the second sub route if the first did not succeed" in {
|
||||||
|
Get() ~> {
|
||||||
|
concat(
|
||||||
|
post { complete("first") },
|
||||||
|
get { complete("second") })
|
||||||
|
} ~> check { responseAs[String] shouldEqual "second" }
|
||||||
|
}
|
||||||
|
"collect rejections from both sub routes" in {
|
||||||
|
Delete() ~> {
|
||||||
|
concat(
|
||||||
|
get { completeOk },
|
||||||
|
put { completeOk })
|
||||||
|
} ~> check { rejections shouldEqual Seq(MethodRejection(GET), MethodRejection(PUT)) }
|
||||||
|
}
|
||||||
|
"clear rejections that have already been 'overcome' by previous directives" in {
|
||||||
|
Put() ~> {
|
||||||
|
concat(
|
||||||
|
put { parameter('yeah) { echoComplete } },
|
||||||
|
get { completeOk })
|
||||||
|
} ~> check { rejection shouldEqual MissingQueryParamRejection("yeah") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"Route conjunction" should {
|
"Route conjunction" should {
|
||||||
val stringDirective = provide("The cat")
|
val stringDirective = provide("The cat")
|
||||||
val intDirective = provide(42)
|
val intDirective = provide(42)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
package akka.http.scaladsl.server
|
package akka.http.scaladsl.server
|
||||||
|
|
||||||
|
import akka.http.scaladsl.server.Directives.reject
|
||||||
import akka.http.scaladsl.util.FastFuture
|
import akka.http.scaladsl.util.FastFuture
|
||||||
import akka.http.scaladsl.util.FastFuture._
|
import akka.http.scaladsl.util.FastFuture._
|
||||||
|
|
||||||
|
|
@ -18,6 +19,17 @@ trait RouteConcatenation {
|
||||||
*/
|
*/
|
||||||
implicit def enhanceRouteWithConcatenation(route: Route): RouteConcatenation.RouteWithConcatenation =
|
implicit def enhanceRouteWithConcatenation(route: Route): RouteConcatenation.RouteWithConcatenation =
|
||||||
new RouteConcatenation.RouteWithConcatenation(route: Route)
|
new RouteConcatenation.RouteWithConcatenation(route: Route)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries the supplied routes in sequence, returning the result of the first route that doesn't reject the request.
|
||||||
|
* This is an alternative to direct usage of the infix ~ operator. The ~ can be prone to programmer error, because if
|
||||||
|
* it is omitted, the program will still be syntactically correct, but will not actually attempt to match multiple
|
||||||
|
* routes, as intended.
|
||||||
|
*
|
||||||
|
* @param routes subroutes to concatenate
|
||||||
|
* @return the concatenated route
|
||||||
|
*/
|
||||||
|
def concat(routes: Route*): Route = routes.foldLeft[Route](reject)(_ ~ _)
|
||||||
}
|
}
|
||||||
|
|
||||||
object RouteConcatenation extends RouteConcatenation {
|
object RouteConcatenation extends RouteConcatenation {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue