Reputation: 1000
The behavior I'm trying to implement using Ring routes is approximately described in this question.
Basically I have some URLs that end with trailing slashes, and I'm trying to create a middleware that will redirect from e.g example.com/foo
to example.com/foo/
if and only if /foo/
is a valid URL and /foo
is not.
I'm currently using this middleware
(defn good-response? [resp]
(and resp (not= (:status resp) 404)))
(defn wrap-slash [handler]
(fn [{:keys [uri] :as req}]
(let [resp (handler req)]
(if (or (good-response? resp) (.endsWith "/" uri))
resp
(let [added-slash (str uri "/")]
(if (good-response? (handler (assoc req :uri added-slash)))
(redirect added-slash)
resp))))))
Which does almost everything it should: It redirects from /foo
to /foo/
iff /foo/
exists and /foo
does not.
My concern is that this solution will call (handler req)
at least twice - once on the request for /foo
and again when the client requests the redirected URL. It's not a problem now, but I could imagine it being painful to double the response time for some slow page with hundreds of DB queries or some such thing.
Is there a way to simply check if a handler exists for a given URL, without calling that handler? Could we avoid the problem entirely by say, making (:body request)
lazy?
Upvotes: 1
Views: 952
Reputation: 21
I fixed your code.
(defn good-response? [resp]
(and resp (not= (:status resp) 404)))
(defn wrap-slash [handler]
(fn [{:keys [uri] :as req}]
(let [resp (handler req)]
(if (or (good-response? resp) (.endsWith uri "/"))
resp
(let [added-slash (str uri "/")]
(if (good-response? (handler (-> req
(assoc :uri added-slash)
(assoc :path-info added-slash))))
(redirect added-slash)
resp))))))
you need to change :path-info.
Upvotes: 1
Reputation: 17773
There is no general way in ring to check for "is this a valid uri?" without calling the whole stack handler with that uri, since there is no central list of uris, but even then handlers can decline to handle a request for any reason at all, not just based on its uri.
I would probably go the other way around; for all handlers that actually need this behavior, make them catch the "unslashed" version too and redirect then if needed/useful.
Or use a separate handler/middleware if the url should ALWAYS end in a slash given some rules and let the redirect fail if it doesn't match. Either way the end user will get a 404, so who cares, really?
But the most specific handlers are usually in the best position to make the decision.
Oh, and you probably don't just forward a POST to some other URI.
Upvotes: 2