Implement #238, handle trailing slashes in frontend match-by-path

This commit is contained in:
Juho Teperi 2019-03-15 17:33:46 +02:00
parent 3644c2e392
commit c86f042f54
2 changed files with 106 additions and 4 deletions

View file

@ -1,5 +1,6 @@
(ns reitit.frontend
(:require [clojure.set :as set]
[clojure.string :as str]
[reitit.coercion :as coercion]
[reitit.coercion :as rc]
[reitit.core :as r])
@ -16,10 +17,18 @@
(defn match-by-path
"Given routing tree and current path, return match with possibly
coerced parameters. Return nil if no match found."
coerced parameters. Returns nil if no match found."
[router path]
(let [uri (.parse Uri path)]
(if-let [match (r/match-by-path router (.getPath uri))]
(let [uri (.parse Uri path)
path (.getPath uri)]
(if-let [match (or (r/match-by-path router path)
(if-let [trailing-slash-handling (:trailing-slash-handling (r/options router))]
(if (str/ends-with? path "/")
(if (not= trailing-slash-handling :add)
(r/match-by-path router (subs path 0 (dec (count path)))))
(if (not= trailing-slash-handling :remove)
(r/match-by-path router (str path "/"))))))]
;; User can update browser location in on-navigate call using replace-state
(let [q (query-params uri)
match (assoc match :query-params q)
;; Return uncoerced values if coercion is not enabled - so
@ -40,7 +49,14 @@
(defn router
"Create a `reitit.core.router` from raw route data and optionally an options map.
Enables request coercion. See [[reitit.core/router]] for details on options."
Enables request coercion. See [[reitit.core/router]] for details on options.
Additional options:
| key | description |
| -------------|-------------|
| :trailing-slash-handling | TODO |
"
([raw-routes]
(router raw-routes {}))
([raw-routes opts]

View file

@ -104,3 +104,89 @@
(capture-console
(fn []
(rf/match-by-name! router ::foo {}))))))))))
(deftest trailing-slash-handling-test
(testing ":both"
(let [router (r/router ["/"
["" ::frontpage]
["foo" ::foo]
["bar/" ::bar]]
{:trailing-slash-handling :both})]
(is (= (r/map->Match
{:template "/foo"
:data {:name ::foo}
:path-params {}
:query-params {}
:path "/foo"
:parameters {:query {}
:path {}}})
(rf/match-by-path router "/foo/")
(rf/match-by-path router "/foo")))
(is (= (r/map->Match
{:template "/bar/"
:data {:name ::bar}
:path-params {}
:query-params {}
:path "/bar/"
:parameters {:query {}
:path {}}})
(rf/match-by-path router "/bar/")
(rf/match-by-path router "/bar"))) ))
(testing ":add"
(let [router (r/router ["/"
["" ::frontpage]
["foo" ::foo]
["bar/" ::bar]]
{:trailing-slash-handling :add})]
(is (= (r/map->Match
{:template "/foo"
:data {:name ::foo}
:path-params {}
:query-params {}
:path "/foo"
:parameters {:query {}
:path {}}})
(rf/match-by-path router "/foo")))
(is (nil? (rf/match-by-path router "/foo/")))
(is (= (r/map->Match
{:template "/bar/"
:data {:name ::bar}
:path-params {}
:query-params {}
:path "/bar/"
:parameters {:query {}
:path {}}})
(rf/match-by-path router "/bar/")
(rf/match-by-path router "/bar")))))
(testing ":remove"
(let [router (r/router ["/"
["" ::frontpage]
["foo" ::foo]
["bar/" ::bar]]
{:trailing-slash-handling :remove})]
(is (= (r/map->Match
{:template "/foo"
:data {:name ::foo}
:path-params {}
:query-params {}
:path "/foo"
:parameters {:query {}
:path {}}})
(rf/match-by-path router "/foo/")
(rf/match-by-path router "/foo")))
(is (= (r/map->Match
{:template "/bar/"
:data {:name ::bar}
:path-params {}
:query-params {}
:path "/bar/"
:parameters {:query {}
:path {}}})
(rf/match-by-path router "/bar/")))
(is (nil? (rf/match-by-path router "/bar"))))))