From f0cbf2545485062f3d49c8821a38f8801d439dcf Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 29 Oct 2013 16:46:10 +0100 Subject: [PATCH] ! routing: add `pathEnd` and `pathEndOrSingleSlash` directive, closes #628 This commit also removes all ambiguity from the `path` and `pathPrefix` directives. From now on all path directives (except for the new `pathEndOrSingleSlash`) match exactly one single (part of an) request URI. --- .../spray/routing/PathDirectivesSpec.scala | 18 +++++++++++++++++- .../routing/directives/PathDirectives.scala | 15 ++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/spray-routing-tests/src/test/scala/spray/routing/PathDirectivesSpec.scala b/spray-routing-tests/src/test/scala/spray/routing/PathDirectivesSpec.scala index f4861f20d0..bb8e7d3db3 100644 --- a/spray-routing-tests/src/test/scala/spray/routing/PathDirectivesSpec.scala +++ b/spray-routing-tests/src/test/scala/spray/routing/PathDirectivesSpec.scala @@ -40,7 +40,7 @@ class PathDirectivesSpec extends RoutingSpec { "reject [/foobar]" in test() "reject [/foo/bar]" in test() "accept [/foo] and clear the unmatchedPath" in test("") - "accept [/foo/] and clear the unmatchedPath" in test("") + "reject [/foo/]" in test() } """path("")""" should { val test = testFor(path("") { echoUnmatchedPath }) @@ -187,6 +187,22 @@ class PathDirectivesSpec extends RoutingSpec { "accept [/bar]" in test("bar") } + """pathPrefix("foo") & pathEnd""" in { + val test = testFor((pathPrefix("foo") & pathEnd) { echoUnmatchedPath }) + "reject [/foobar]" in test() + "reject [/foo/bar]" in test() + "accept [/foo] and clear the unmatchedPath" in test("") + "reject [/foo/]" in test() + } + + """pathPrefix("foo") & pathEndOrSingleSlash""" in { + val test = testFor((pathPrefix("foo") & pathEndOrSingleSlash) { echoUnmatchedPath }) + "reject [/foobar]" in test() + "reject [/foo/bar]" in test() + "accept [/foo] and clear the unmatchedPath" in test("") + "accept [/foo/] and clear the unmatchedPath" in test("") + } + "PathMatchers" should { import shapeless._ "support the hmap modifier" in { diff --git a/spray-routing/src/main/scala/spray/routing/directives/PathDirectives.scala b/spray-routing/src/main/scala/spray/routing/directives/PathDirectives.scala index 874726a3df..9c4ec9aa9d 100644 --- a/spray-routing/src/main/scala/spray/routing/directives/PathDirectives.scala +++ b/spray-routing/src/main/scala/spray/routing/directives/PathDirectives.scala @@ -30,7 +30,7 @@ trait PathDirectives extends PathMatchers with ImplicitPathMatcherConstruction { * or leave only a single trailing slash. * If matched the value extracted by the PathMatcher is extracted on the directive level. */ - def path[L <: HList](pm: PathMatcher[L]): Directive[L] = pathPrefix(pm ~ (Slash?) ~ PathEnd) + def path[L <: HList](pm: PathMatcher[L]): Directive[L] = pathPrefix(pm ~ PathEnd) /** * Tries to consume a leading slash from the unmatched path of the [[spray.routing.RequestContext]] @@ -91,6 +91,19 @@ trait PathDirectives extends PathMatchers with ImplicitPathMatcherConstruction { case Matched(_, values) ⇒ hprovide(values) case Unmatched ⇒ reject } + + /** + * Rejects the request if the unmatchedPath of the [[spray.RequestContext]] is non-empty, + * or said differently: only passes on the request to its inner route if the request path + * has been matched completely. + */ + def pathEnd: Directive0 = rawPathPrefix(PathEnd) + + /** + * Only passes on the request to its inner route if the request path has been matched + * completely or only consists of exactly one remaining slash. + */ + def pathEndOrSingleSlash: Directive0 = rawPathPrefix(Slash.? ~ PathEnd) } object PathDirectives extends PathDirectives \ No newline at end of file