juxt / bidi

Bidirectional URI routing
MIT License
991 stars 92 forks source link

Route Segments with spaces in them dont get matched... #147

Open twillis opened 7 years ago

twillis commented 7 years ago

Was trying out some things because I want to move an app off of secretary. This could be a misunderstanding on my part. but this is surprising that path-for can give me a url that wont match.

(ns apollo.client.routes
  (:require [bidi.bidi :as bidi]))

(def routes ["/" {"" :home-page
                 ["artists/" :artist] {"" :artist-detail
                                       ["/albums/" :album] :album-detail}
                 true :not-found}])

(def test-route "/artists/Faith No More/albums/Angel Dust")
(= test-route (bidi/path-for routes :album-detail :artist "Faith No More" :album "Angel Dust")) # true
(bidi/match-route routes test-route)  # {:handler :not-found}
solussd commented 7 years ago

If you URLEncode, it still doesn't work. :\

solussd commented 7 years ago

@twillis I found a workaround. If you use [#".+" :artist] and [#".+" :album] instead of the bare keywords it'll work. :)

twillis commented 7 years ago

@solussd thanks for the suggestion, I will give that a shot.

manuel-uberti commented 7 years ago

@solussd thanks for the workaround, I was really struggling to understand how to deal with spaces and colons.

mpenet commented 6 years ago

Another workaround if you want to have this applied globally is to overwrite the protocol for matching pattern segments:

In my case I had to allow colons on dozens of endpoints and did it that way:

    (extend-protocol bidi.bidi/PatternSegment
      clojure.lang.Keyword
      (segment-regex-group [_] "[A-Za-z0-9\\-\\_\\.\\:]+")
      (param-key [this] this)
      (transform-param [_] identity)
      (unmatch-segment [this params]
        (if-let [v (this params)]
          (bidi.bidi/encode-parameter v)
          (throw (ex-info (str "Cannot form URI without a value given for "
                               this " parameter")
                          {})))))

Maybe that could mean this would be nicer to have segment-regex-group in a separate protocol to avoid the repetition of the other functions involved.

harold commented 2 years ago

Thanks for bidi! It's lovely, we use it every day, and if it didn't exist we'd have to start by inventing it.

@twillis I found a workaround. If you use [#".+" :artist] and [#".+" :album] instead of the bare keywords it'll work. :)

This workaround gets it to match, but for me, in cljs, it's breaking the :route-params:

cljs.user=> (require '[bidi.bidi :as bidi])
nil
cljs.user=> (def routes ["/" [["a/" {[:b] :a}]]])
#'cljs.user/routes
cljs.user=> (bidi/match-route routes "/a/b")
{:route-params {:b "b"}, :handler :a}

Cool.

cljs.user=> (bidi/match-route routes "/a/b%20f")
nil

That's this issue, %20 causes it to fail to match.

cljs.user=> (def routes ["/" [["a/" {[#".+" :b] :a}]]])
#'cljs.user/routes
cljs.user=> (bidi/match-route routes "/a/b%20f")
{:route-params {:b "f"}, :handler :a}

This now matches, but I expected :route-params {:b "b f"} --- perhaps I did the regex wrong?

cljs.user=> (def routes ["/" [["a/" {[#"(.+)" :b] :a}]]])
#'cljs.user/routes
cljs.user=> (bidi/match-route routes "/a/b%20f")
{:route-params {:b "b "}, :handler :a}
cljs.user=> 

I tried to get clever with the parens, but now the route param is "b " (it dropped the last character of the string, so the 'f' was lost).

Hopefully someone here can help! Regardless, thanks to all for your time and consideration.

jrwdunham commented 6 months ago

Wow, I just lost too much time trying to figure out why a url-encoded email in the path parms works in clojure but not clojurescript with[#".*" :email]. Thanks for the workaround @harold