Liftの携帯対応まとめ

Liftでケータイ対応するためのメモ。テンプレートの切り替え、リクエスト・レスポンスのSJIS対応など。検証バージョンはLift2.0-M5です。

■User-Agentの取得方法

 val userAgent:String = S.request.get.userAgent.get

など。
但し、Sオブジェクトの準備ができていないフェーズ(earlyフェーズなど)ではS.requestはEmptyなので、

 val userAgent:String = request.asInstanceOf[HTTPRequest].header("User-Agent").open_!

などとします。
LiftはServletコードを分離するように構成されているので、生のServletRequestを触りたければ、

 net.liftweb.http.Req
 net.liftweb.http.provider.HTTPRequest
 net.liftweb.http.provider.servlet.HTTPRequestServlet

これらの階層のAPIを追うことになります。

■レスポンスをSJISに変換

↓pomu0325さんのエントリに全て書いてあります。大変参考になりました!
Liftのテンプレートでutf-8以外のレスポンスを返す

  class Boot {
    def boot {
        :
      LiftRules.responseTransformers.append( responseEncoding )
        :
    }
  }

  //携帯ならレスポンスをSJISに変換
  private def responseEncoding(org: LiftResponse): LiftResponse = {
    if( isMobile(userAgent) ){
      org match {
        case x: XhtmlResponse =>
          S.skipXmlHeader = true
          val m = x.toResponse
          val h = x.headers ::: ("Content-Type", "text/html; charset=Shift_JIS") :: Nil
          InMemoryResponse(new String(m.data, "utf-8").getBytes("Shift_JIS"), h, m.cookies, m.code)
        case _ => org
      }
    }else{
      org
    }
  }

■リクエストパラメータをShift_JISに変換
  class Boot {
    def boot {
        :
      LiftRules.early.append( requestEncoding )
        :
    }
  }

  //携帯ならリクエストパラメータはSJIS
  private def requestEncoding(request: HTTPRequest):Unit={
    if( isMobile(userAgent) ){
      request.setCharacterEncoding("Shift_JIS")
    }else{
      request.setCharacterEncoding("UTF-8")
    }
  }

POSTパラメータはこれでOK。

■GETパラメータに注意

GETのShift_JISパラメータがURLエンコードされてきます。
UTF-8以外のURLエンコードをサポートするかどうかはJavaコンテナ依存とされているので、コンテナの仕様を調査。

Tomcatの場合は、server.xmlで、

  <Connector useBodyEncodingForURI="true"/>

Jettyの場合は、
マニュアルを参考に、次のようにコードを直す。

  //携帯ならリクエストパラメータはSJIS
  private def requestEncoding(request: HTTPRequest):Unit={
    import _root_.org.mortbay.jetty.Request
    if( isMobile(userAgent) ){
      request.asInstanceOf[HTTPRequestServlet].
        req.asInstanceOf[Request].setQueryEncoding("Shift_JIS")
      request.setCharacterEncoding("Shift_JIS")
    }else{
        request.asInstanceOf[HTTPRequestServlet].
        req.asInstanceOf[Request].setQueryEncoding("UTF-8")
        request.setCharacterEncoding("UTF-8")
    }
  }

本来なら、ServletFilterなどで処理して、Jetty依存コードは追い出すのが良いと思います。

これだけではまだイケてなくて、S.paramは文字化けしたままなので、S.paramではなく生のHttpServletRequestから値を取得します。

  object word extends RequestVar[String]( directParam("word") )

  //SJISのGETパラメータの場合、S.paramから値を取れない。直接HttpRequestから取得すればOK。
  def directParam(name:String):String = {
    val value = S.request.open_!.
      request.asInstanceOf[HTTPRequestServlet].
      req.getParameter(name)
    if( value != null ) value else ""
  }

■ケータイ用のテンプレートに切り替える

PCブラウザでのページ構成をケータイ用にするケース。"mobile/"フォルダにPC版と同じ構成のテンプレートを用意します。

  class Boot {
    def boot {
      :
    LiftRules.viewDispatch.append {
      case i1@List("index") => selector( i1 )
      case i2@List("catalog", _ ) => selector( i2 )
    }
      :
    }
  }

List("index")などは、SiteMapに登録するURL表現。これにヒットするURLに、PCなら/index.htmlを、mobileなら /mobile/index.htmlを適用します。

  //携帯ならmobile/フォルダ配下のテンプレートを選択
  def selector(list:List[String]): Either[() => Box[scala.xml.NodeSeq], LiftView] = {
    if( isMobile( userAgent ) ){
      Left( () => TemplateFinder.findAnyTemplate( "mobile"::list ) )
    }else{
      Right( new LiftView{
        def dispatch = {
          case _ => () => TemplateFinder.findAnyTemplate( list )
        }
      })
    }
  }

他にもやり方いろいろあると思いますが、考えた結果、一番ラクそうなのはこんな感じでした。

■autoIncludeAjax

Liftではデフォルトで、コールバックなどのステートフルオブジェクトを監視するために、AjaxでPingを打つようになっています。
(ログに出てくる"GET:/ajax_request/liftAjax.js"がそれ)
これは特にケータイには不要。外すには、

  class Boot {
    def boot {
      LiftRules.autoIncludeAjax = { session => false }
    }
  }

などとしますが、条件付きで外したい場合は、以下のように設定する。

  LiftRules.autoIncludeAjax = { session => {
    if( isMobile( userAgent ) ) false else true
  }}

Lift超絶便利ですね! ;x; ブワッ

■余談

Scala2.8対応が待ち遠しかったLift2.0ですが、Scala2.8の開発遅延のため、Lift2.0はターゲットをScala2.7.7でFIXするそうです。残念ですが、しょうがないです。。
参照:Lift Release 2.0 count-down


コメント

コメントしてください
お名前:
入力しなければ「匿名さん」。20字以内。

メール:
入力しても表示しません

URL:
入力すればリンクが貼れます


コメント: