Compojure + Fleetで文字化け


久しぶりにCompojure + Fleetな環境を触っていて何気なく日本語が記載されたテンプレートを表示してみようかと思い、早速テンプレートに日本語を追加して表示してみたところ、「??」という風に見事に文字化けが発生してしまいました。

広告

その時のコードはこんな感じです。

core.clj

(ns compojuretest.core
  (:use compojure.core
        compojure.response
        ring.adapter.jetty
        [ring.util.response :only (response content-type)]
        fleet)
  (:require [compojure.route :as route]))
 
;; =============================================
;; Fleet Setup 
;; =============================================
(def filters [ 
  "js" :str
  "html" :xml
  "fleet" :bypass])
 
(fleet-ns tpl "templates" filters)
 
(extend-protocol Renderable
  fleet.util.CljString
  (render [this _] (response (.toString this))))
 
;; =============================================
;; Routes
;; =============================================
(defroutes app 
  (GET "/" [] (tpl/index {})))
 
;; =============================================
;; Run Server
;; =============================================
(defn -main []
  (let [port (Integer/parseInt (get (System/getenv) "PORT" "8080"))]
    (run-jetty app {:port port})))

index.html.fleet

< !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>テスト</title>
</head>
<body>
<h1>テスト</h1>
</body>
</html>

何がおかしいのか調べていて、通常のサイトとのレスポンスヘッダーを見比べてみると、Content-Typeのヘッダーがありませんでした。そこで、下記のようにしてあげると正常にContent-Typeヘッダーも生成されるようになり、文字化けが解消されました。

;; =============================================
;; Routes
;; =============================================
(defroutes app
  (GET "/" [] {:headers {"Content-Type" "text/html; charset=utf8"}
               :body (str (tpl/index {}))}))

毎回headersを書くのが怠いという方は、下記だけでも大丈夫のようです。まだ、Compojureの中身を見ていないので、変なやり方かもしれませんが。

;; =============================================
;; Routes
;; =============================================
(defroutes app
  (GET "/" [] (str (tpl/index {}))))

上記のFleet Setupで行っているextend-protocol部分でうまいこと纏め上げられるんじゃないかとも思いますがとりあえず、ベタであろうやり方を書いてみました。

追記:2011-11-17 10:31
Compojureのresponse.cljを見たところ、Stringの場合はContent-Typeを指定しているのでstr関数を挟む場合に手動で指定する必要はないようです。

src/compojure/response.clj

(extend-protocol Renderable
  nil
  (render [_ _] nil)   
  String
  (render [body _]
    (-> (response body)
        (content-type "text/html; charset=utf-8")))
  APersistentMap
  (render [resp-map _]
    (merge (response "") resp-map))
  IFn
  (render [func request]
    (render (func request) request))
  IDeref  
  (render [ref request]
    (render (deref ref) request)) 
  File      
  (render [file _] (response file))
  ISeq
  (render [coll _] (-> (response coll)
                       (content-type "text/html; charset=utf-8")))
  InputStream
  (render [stream _] (response stream))
  URL
  (render [url _]
    (if (= "file" (.getProtocol url))
      (response (io/as-file url))
      (response (io/input-stream url)))))

追記:2011-11-17 10:39
extend-protocolを下記のように編集することで、str関数を挟まなくても正常に表示されるようになりました。

(extend-protocol Renderable
  fleet.util.CljString
  (render [this _] (-> (response (.toString this))
                       (content-type "text/html; charset=utf-8"))))

関連記事