Play! frameworkでモバイルサイトを作る

冬休みの宿題ということで、前からぼちぼち作っているサイトのモバイル版を作ろうと思った。モバイル向けの部分はjQuery Mobileを使うことにした(これに関しては別エントリーで)。

環境

  • Play! framework 2.2

(主に)3種類の選択肢

desktopとmobile (smart phone)の両方に対応したサイトを作る場合、主に以下の3通りがある。

  1. 同じURL。User-Agentで判断して異なるHTML、CSS、JavaScriptを返す。
  2. 異なるURL。(www.example.com と m.example.com など)
  3. 同じURL。レスポンシブデザイン。

(3は今の自分がやるには面倒なのでとりあえず除外するとして)、どれがいいんだろう?と思ってちょっとググってみたところ、Googleのこんなページが見つかった。

正直、ユーザーにとっては同じURLだろうと異なるURLだろうとそんな気にしないだろうから(自動的にリダイレクトしてくれれば)、SEOという観点は重要かなと思った。

で、結論として1番の同じURL、異なるコンテンツ、という方式を選ぶことにした。

同じURL、異なるコンテンツの場合にやるべきこと

さっきのGoogleのページに書いてあるけど、同じURL、異なるコンテンツの場合、Vary: User-Agent というヘッダーを返して、User-Agentによってページの内容が異なるという事を教えてあげることが推奨されている。具体的な方法については、本エントリーの後ろのほうで説明する。

コンテンツの出し分け方

まずはコンテンツの出し分け方を説明。特に迷う所ではないと思うので、さらっと。

異なるコンテンツを用意

まずは、view, JavaScript, CSSを、デスクトップPC用、モバイル用の2種類を用意する。別々のフォルダにしておいたほうがいいかな。layout、headerなども別々にしておく。

例えば以下のようにする。まずはPCサイト。

@* views/desktop/_layout.html.scala *@
@(content: Html)
<html>
<head>
  <title>@meta.title</title>
  <!-- この辺でPC向けのCSSとかを読み込み -->
</head>
<body>
  @content
</body>
</html>
@* views/desktop/index.html.scala *@

@import views.html.desktop._

@_layout {
    PC向けサイトだよ
}

次にモバイルサイト。

@* views/mobile/_layout.html.scala *@
@(content: Html)
<html>
<head>
  <title>@meta.title</title>
  <!-- この辺でmobile向けのCSSとかを読み込み -->
</head>
<body>
  @content
</body>
</html>
@* views/mobile/index.html.scala *@

@import views.html.mobile._

@_layout {
    モバイル向けサイトだよ
}

ユーザーエージェントを判定して、出し分け

PCからのアクセスかモバイル(スマートフォン)からのアクセスかを判別する。この辺はみんなライブラリにしてるかと思うけど、こんな感じ。

object UserAgent {
  def isMobile(implicit requestHeader: RequestHeader): Boolean = {
    requestHeader.headers.get("user-agent").map { uaString =>
      // uaStringを元に判別して、trueかfalseを返す
      true
    } getOrElse (false) // user-agentがなければ、false
  }
}

これをコントローラー内で使う。

object FooController extends Controller {
  def index = Action { implicit request =>
    if (UserAgent.isMobile) {
      Ok(views.html.mobile.index)
    } else {
      Ok(views.html.desktop.index)
    }
  }
}

Varyヘッダーを出力

全てのページでPCとスマホ向けの内容が異なるのであれば、Play! framworkのフィルターを使えばいいけど、今回はページによってはPCもスマホも共通なので、コンテンツが異なるページの場合だけVaryヘッダーを出したい。ということで、Action compositionを使うことにした。

Varyヘッダーを出力するActionを作成

以下のようにActionを作成。詳細はさきほどのドキュメントを参照。

import scala.concurrent.Future
import play.api.mvc._
import play.api.libs.concurrent.Execution.Implicits._
import play.api.http.HeaderNames._

case class MultiDevice [A](action: Action[A]) extends Action[A] {
  def apply(request: Request[A]): Future[SimpleResult] = {
    action(request) map { result =>
      result.withHeaders(VARY -> "User-Agent")
    }
  }

  lazy val parser = action.parser
}

Actionを合成

先ほどのコントローラーのコードを以下のように修正すればOK。

object FooController extends Controller {
  def index = MultiDevice { Action { implicit request =>
    if (UserAgent.isMobile) {
      Ok(views.html.mobile.index)
    } else {
      Ok(views.html.desktop.index)
    }
  }}
}

まとめ

desktop PCとmobile(スマホ)の両方に対応したサイトを作る方法は、大雑把に3通りある。同じURLでデバイス毎に異なるコンテンツを提供する場合には、Vary: User-Agent ヘッダを出力したほうがいいらしい。Play! frameworkだと割と簡単に出来るよ、と。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です