例のごとくドキュメントが少ないので、Liftのソースを追ってみます。通常、テンプレートを使用したページは、XhtmlResponseとしてNodeが保持され、LiftServletの最後にtoResponseでInMemoryResponseに変換(*1)してから送られます。
// net/liftweb/http/LiftServlet.scala(l.183) resp match { case Full(cresp) => val resp = cresp.toResponse // *1 LiftResponse.toResponseはInMemoryResponseを返す logIfDump(req, resp) sendResponse(resp, response, Full(req)) //...後略...
問題1:文字コード
XhtmlResponseのtoResponseで何をやっているのか見てみると、mixinしているNodeResponse.toResponseで、utf-8とハードコードされています…。// net/liftweb/http/LiftResponse.scala(l.415) InMemoryResponse(ret.getBytes("UTF-8"), headers, cookies, code)
問題2:Content-Type
次、Content-Typeにもcharset=utf-8が勝手に出てしまいます。どこでやっているかと調べると、LiftServlet.sendResponseでContent-Typeをセットしています。determineContentTypeはここでもまたutf-8がハードコードです。ただし、ここでは明示的にContent-Typeを指定しておけば、上書きはしないようです。// LiftServlet.scala(l.482) // insure that certain header fields are set val header = insureField(fixHeaders(resp.headers), List(("Content-Type", // *2 LiftRules.determineContentType(pairFromRequest(request))), ("Content-Length", len.toString))) // LiftRules.scala(l.168) @volatile var determineContentType: PartialFunction[(Box[Req], Box[String]), String] = { case (_, Full(accept)) if this.useXhtmlMimeType && accept.toLowerCase.contains("application/xhtml+xml") => "application/xhtml+xml; charset=utf-8" case _ => "text/html; charset=utf-8" }
- insureFieldはnet.liftweb.util.HttpHelpersのメソッド。第1引数のList[Pair]に第2引数のList[Pair]の_1が含まれてない場合に、追加する。
問題3:XML宣言
さらに、<?xml version="1.0" encoding="UTF-8"?>のXML宣言も必ず付けてくれるようです。が、S.skipXmlHeaderをtrueにしておけば、何もしないようです。// net.liftweb.http.LiftRules.scala(l.442) @volatile var calculateXmlHeader: (NodeResponse, Node, Box[String]) => String = { case _ if S.skipXmlHeader => "" case (_, up: Unparsed, _) => "" case (_, _, Empty) | (_, _, Failure(_, _, _)) => "\n" case (_, _, Full(s)) if (s.toLowerCase.startsWith("text/html")) => "\n" case (_, _, Full(s)) if (s.toLowerCase.startsWith("text/xml") || s.toLowerCase.startsWith("text/xhtml") || s.toLowerCase.startsWith("application/xml") || s.toLowerCase.startsWith("application/xhtml+xml")) => "\n" case _ => "" }
対策
LiftRulesに、responseTransformersというものがあり、ここでLiftResponseが送られる前に書き換えることができます。最終的に、Boot.scalaで以下の様なことをしてやると、好みの文字コードでレスポンスを返すことができました。def boot { //...省略... LiftRules.responseTransformers.append(conv2sjis) } private def conv2sjis(org: LiftResponse): LiftResponse = { org match { case x: XhtmlResponse => S.skipXmlHeader = true // *1 val m = x.toResponse // *2 val h = x.headers ::: ("Content-Type", "text/html; charset=Shift_JIS") :: Nil // *3 InMemoryResponse(new String(m.data, "utf-8").getBytes("Shift_JIS"), h, m.cookies, m.code) // *4 case _ => org } }
- XML宣言を付けないようにS.skipXmlHeader=trueにセット。
- 一度、InMemoryResponseに変換
- Content-TypeヘッダをListに追加。
- SJISに変換し、新たなInMemoryResponseを返す。(LiftResponseはケースクラスなので書き換えられない)
0 件のコメント:
コメントを投稿