Play! frameworkでモバイルサイトを作る
冬休みの宿題ということで、前からぼちぼち作っているサイトのモバイル版を作ろうと思った。モバイル向けの部分はjQuery Mobileを使うことにした(これに関しては別エントリーで)。
環境
- Play! framework 2.2
(主に)3種類の選択肢
desktopとmobile (smart phone)の両方に対応したサイトを作る場合、主に以下の3通りがある。
- 同じURL。User-Agentで判断して異なるHTML、CSS、JavaScriptを返す。
- 異なるURL。(www.example.com と m.example.com など)
- 同じ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だと割と簡単に出来るよ、と。