Adding compojure.middleware.wrap-canonical-redirect

82 views
Skip to first unread message

lynch513

unread,
Dec 24, 2017, 3:42:28 PM12/24/17
to Luminus
Hi.

How can I add compojure.middleware.wrap-canonical-redirect in Luminus? I need redirect 301 from URI with trailing slash to URI without it. For example:

http://localhost:3000/testA/ -> http://localhost:3000/testA

I tried to add at middleware middleware/wrap-base like this:

(defn wrap-base [handler]
    (-> ((:middleware defaults) handler)
        wrap-canonical-redirect  ;; <------ Add here
        wrap-webjars
        wrap-flash
        (wrap-session {:cookie-attrs {:http-only true}})
        (wrap-defaults
          (-> site-defaults
              (assoc-in [:security :anti-forgery] false)
              (dissoc :session)))
        wrap-context
        wrap-internal-error))

Yes it's ok and it's work. Except response return 301 status code, but wrong content type application/octet-stream but it is necessary to text/html. This means that I need write my own middleware and add required headers manually? 

Could you tell me please write way to do this?

Dmitri

unread,
Dec 24, 2017, 4:09:45 PM12/24/17
to Luminus
Hi,

The middleware doesn't set a content type, and the cotet-stream is the default type returned by Ring. You can wrap the middleware with your own as follows to set the content type, e.g:

(ns ...
  (:require
    ...
    [compojure.middleware :refer [wrap-canonical-redirect]]
    [ring.util.http-response :as response]))

(defn wrap-canonical-redirect-html [handler]
  (let [canonical-handler (wrap-canonical-redirect handler)]
    (fn [req]
      (-> (canonical-handler req)
          (response/header "Content-Type" "text/html; charset=utf-8")))))

lynch513

unread,
Dec 27, 2017, 10:25:46 AM12/27/17
to Luminus
Thanks. Looks good. I test it and it works but that code brokes main route "/" defined in:

(defroutes home-routes
   
(GET "/" [] (some-handler))
...)

Our wrap-canonical-redirect middleware remove trailng slash and we get 404 error :) I think it's problem Compojure, not Luminus

At now moment I removed this middleware and tune 301 redirect on front server Nginx from URL with trailing slash. Perhaps this is a good solution. In my case it is SEO requirements. 
But what about case when user hasn't front server and he want all-in jar solution?

PS
I found Clojure and Luminus recently. It is very interesting framework and maybe I just didn't have enough knowledge about it. 
I noticed that frameworks like Express and Scotty (Sinatra inspaired) has another behavior. For example if we define "/test-route" we catch "/test-route" and "/test-route/" too without 404 error.


понедельник, 25 декабря 2017 г., 0:09:45 UTC+3 пользователь Dmitri написал:

Dmitri

unread,
Dec 28, 2017, 1:06:11 PM12/28/17
to Luminus
Yeah some frameworks are a bit smarter out of the box, Luminus is less opinionated in that regard. :)

I think compojure wrap-canonical-redirect could be smarter to leave the / in place if that's the only path element. It's easy enough to handle with the custom wrapper as follows:

(-> (canonical-handler req)
   
(update :uri #(or (not-empty %) "/"))
    (response/header "Content-Type" "text/html; charset=utf-8"))

This will ensure that the URI has at least a single segment.

Dmitri

unread,
Dec 28, 2017, 1:08:09 PM12/28/17
to Luminus
Another thing worth noting is that you can apply middleware selectively to different route groups.

Dmitri

unread,
Dec 28, 2017, 1:31:21 PM12/28/17
to Luminus
Actually sorry, that won't work since the canonical handler already wraps the handler. This would need to be handled by passing a custom function to the middleware:

(defn make-canonical [url]
 
(if (> (count url) 1)
   
(if (.endsWith url "/")
     
(.substring url 0 (dec (.length url)))
      url
)
   
"/"))

(defn wrap-canonical-redirect-html [handler]
 
(let [canonical-handler (wrap-canonical-redirect handler make-canonical)]

   
(fn [req]
     
(-> (canonical-handler req)
         
(response/header "Content-Type" "text/html; charset=utf-8")))))

lynch513

unread,
Dec 30, 2017, 11:03:46 AM12/30/17
to Luminus
It all works perfect. Also I add type hint on function argument for performance in my project

(defn make-canonical [^String url]

 
(if (> (count url) 1)
   
(if (.endsWith url "/")
     
(.substring url 0 (dec (.length url)))
      url
)
   
"/"))

Many thanks for helping me. Happy New Year!


четверг, 28 декабря 2017 г., 21:31:21 UTC+3 пользователь Dmitri написал:

Dmitri

unread,
Dec 30, 2017, 11:11:55 AM12/30/17
to Luminus
Great to hear, and happy new year! :)
Reply all
Reply to author
Forward
0 new messages