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


同じカテゴリのエントリ
1.Lift再入門 / 8.javascriptからsubmitできない / 7.Ajax Form / 6.Radio、Checkboxについて / 5.行列型の編集FORM / 4.サーバーサイドバリデーションとサーバサイド関数 / 3.ログインFORM - S.param使ったら負け / 2.Snippetメソッドとして許される型 / sbt0.12.xで依存jar抽出タスク / scala2.10+lift2.5+NetBeans7.2 / Scalaで入門関数プログラミング / reactive-webを試してみました / Lift2.2M1のテンプレート機能 / Scala Compiler Plugin / View Bound/Context Bound / ScalaZa01参加してきました / Akka Frameworkチュートリアルの次 / Akka Frameworkチュートリアルその2 / Akka Frameworkチュートリアル / LiftでJCaptcha / Url Rewrite Filter / sbt-android-plugin / Android SDK for Scala / 祝Lift2.0リリース / Liftの携帯対応まとめ / Scala2.8への移行 / Lift 2.0-scala280-SNAPSHOT/sbt0.7.1 / Scalaお絵かき環境 - Kojo / Lift+Quartzでバッチ / Scala&Liftを採用した理由 / Liftでdate_select系ヘルパーを作る / LiftでAjax / LiftのSubmitかしこい / lift-mapperのpaginateを使う / snippetをspecする / Lift Mapperを拡張する / LiftのDBをMySQLに / Liftプロジェクト環境を整える / Scala本読み比べてみました / NetBeans6.7&scala / じつはScalaはライトな言語 / Scalaバザ~ル / lift1.0所感 / specsを読む / implicit def / ScalaならNetBeansがサイコー / scala勉強会@東北がスタート / それでも俺はLiftをやるってのか / Scala&Liftセットアップ / ブログリニューアル /
コメント

コメントしてください

closed.