Search code examples
clojurecompojure

Compojure regex for matching a trailing slash


Maybe I'm just an idiot, but I can't set up a match for an optional trailing slash in Clojure.

lein repl
REPL started; server listening on localhost port 47383
user=> (use 'ring.mock.request 'clout.core)
nil
user=> (route-matches "/article/" (request :get "/article/"))
{}
user=> (route-matches "/article/?" (request :get "/article"))
nil
user=> (route-matches "/article/?" (request :get "/article/"))
nil
user=> (route-matches #"/article/?" (request :get "/article/"))
java.lang.IllegalArgumentException: No implementation of method: :route-matches of protocol: #'clout.core/Route found for class: java.util.regex.Pattern (NO_SOURCE_FILE:0)

What regex can I use to match an optional trailing slash in Compojure?


Solution

  • The path string expected by clout as the first argument to route-matches is not a regex, but a string that can contain keywords and the * wildcard.

    I believe clout doesn't natively support defining routes that ignore a trailing slash. You could solve the problem with a middleware function that removes trailing slashes. The following functions were taken from an old version of the compojure source code (before the big refactoring), I couldn't find out if they moved to a new place. Here is the original commit that introduced these functions.

    (defn with-uri-rewrite
      "Rewrites a request uri with the result of calling f with the
       request's original uri.  If f returns nil the handler is not called."
      [handler f]
      (fn [request]
        (let [uri (:uri request)
              rewrite (f uri)]
          (if rewrite
            (handler (assoc request :uri rewrite))
            nil))))
    
    (defn- uri-snip-slash
      "Removes a trailing slash from all uris except \"/\"."
      [uri]
      (if (and (not (= "/" uri))
               (.endsWith uri "/"))
        (chop uri)
        uri))
    
    (defn ignore-trailing-slash
      "Makes routes match regardless of whether or not a uri ends in a slash."
      [handler]
      (with-uri-rewrite handler uri-snip-slash))