How to do HTTP 302 redirects with Noir Web framework
Asked Answered
F

2

6

I'm helping to set up a Web site with Clojure's Noir framework, though I have a lot more experience with Django/Python. In Django, I'm used to URLs such as

http://site/some/url 

being 302-redirected automagically to

http://site/some/url/

Noir is more picky and does not do this.

What would be the proper way to do this automatically? Since good URLs are an important way of addressing into a site, and many users will forget the trailing slash, this is basic functionality I'd like to add to my site.

EDIT: Here is what finally worked for me, based on @IvanKoblik's suggestions:

(defn wrap-slash [handler]
  (fn [{:keys [uri] :as req}]
    (if (and (.endsWith uri "/") (not= uri "/"))
      (handler (assoc req :uri (.substring uri
                                0 (dec (count uri)))))
      (handler req))))
Frogmouth answered 18/9, 2012 at 3:16 Comment(3)
Could you elaborate a little more? For example URL to this question on StackOverflow does not have trailing slash.Bridie
Correct - though if you add one the URL still works correctly, which is not the case in my Noir example. I would like to handle both cases with the same view/route code.Frogmouth
I see, I have updated my answer with another possible solution.Bridie
B
2

I think this may be possible with a custom middleware. noir/server has public function add-middleware.

Here's a page from webnoir explaining how to do that.

Judging by the source code this custom middleware is executed first, so you'd be on your own in terms of sessions, cookies, url params, etc.


I wrote a very silly version of the middleware wrapper that checks if request URI ends with slash and if not redirects to URI with slash at the end:

(use [ring.util.response :only [redirect]])

(defn wrap-slash [handler]
  (fn [{:keys [uri] :as req}]
    (if (.endsWith uri "/")
      (handler req)
      (redirect
       (str uri "/")))))

I tested it on my ring/moustache web app and it worked reasonably well.


EDIT1 (Expanding my answer after your reply to my comment.)

You could use custom middleware to either add or strip URL of trailing slash. Just do something like this to strip away trailing slash:

(use [ring.util.response :only [redirect]])

(defn add-slash [handler]
  (fn [{:keys [uri] :as req}]
    (if (.endsWith uri "/")
      (handler (assoc req 
                      :uri (.substring uri 
                                       0 (dec (count uri)))))
      (handler req))))
Bridie answered 18/9, 2012 at 8:2 Comment(7)
Your first solution worked for me after adding (server/add-middleware wrap-slash). Why is the code in EDIT1 needed? Thanks!Frogmouth
@Frogmouth From your reply I realized that it was not essential for you redirect to /some/url/, I thought that you just wanted to make sure that /some/url and /some/url/ led to same responses. Please be careful with the first solution as I think it breaks on URLs with parameters.Bridie
Thanks -- can you be a little more explicit how you would use the second solution you proposed? Thanks again.Frogmouth
@Frogmouth Happy to help! I have updated my answer with a complete second example.Bridie
Hmmm -- I did try the new code and Noir raises a 404 error. The first version works, however.Frogmouth
I guess it's because application expects URLs to have trailing slash.Bridie
Modified version posted in original questionFrogmouth
M
1

I found this useful:

(defpage "" []
  (response/redirect "/myapp/"))

/myapp -> /myapp/

Mcgowen answered 30/8, 2013 at 2:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.