この内容は Blazor Advent Calendar 2020の20日目の記事です
BlazorとOGP
BlazorはSPAフレームワークです。SPAは通常OGP対策が必要です。OGPとは簡単に言えばFacebookやTwitterにURLを貼った場合にサムネイルの画像と簡単な説明を出してくれるあの機能です。Blazorで作った場合、index.htmlにOGPの設定をしてページ毎にjsでその設定を書き換えても、OGPクローラーはjsの書き換えを解釈してくれないので、結局全てのページがindex.htmlの情報として扱われてしまいます。
BlazorとSEO
SPAでもう一つ問題になるのがSEOです。SEOは簡単に言えばGoogleの検索結果を上位にするための技術です。実はSEOに関しては、jsでtitleを書き換えてさえいれば、Googleクローラは対応してくれるようです。自分のサイトもtitleなどの書き換えだけしてそれ以外特に何もしていないですが、ちゃんとtitleなどがページ毎に書き換わって検索にヒットしました。有難うGoogle。残念、Facebook、Twitter他。
<head><title>マンガ読んだ!! - 自分が読んだ漫画のログを残すサービス</title>functionsetTitle(title){document.title=title;//jsでtitleを書き換えればgoogleクローラーはちゃんとそれを見てくれた}BlazorのOGP対策が見つからない
さて、BlazorのOGP対策についてピンポイントでやり方が書いてあるサイトは見つかりませんでした。自分でもどうしたものかなと持っている時に丁度Qittaでバズっている記事を見ました。SNS映えするWebアプリを...!FirebaseとVue.jsでSPAのOGP画像の動的生成をやってみたら案外楽だったです。Vue.jsの記事ですが、これを元に考えていけば何とかなりそうな気がしました。
OGPを動的に生成したいパスのみ、Hostingの設定でFunctionsを呼び出す
先ずポイントとしてOGPを動的に生成したいパスのみ、Hostingの設定でFunctionsを呼び出すを参考に考えました。特に図はかなり何度も見ました。書かれている通りのアプローチもあると思いますが、突き詰めるとサーバサイドでjsによるリダイレクトを使って、人間とクローラーで処理を分けさえすれば他の方法でも行けそうと思いました。
Blazorでサーバとクライアントで同名のルーティングを書いてみる
Blazorは、テンプレート通りに作ればclientとserverが分かれます。サーバ側はページルーティングはなく、クライアントでページルーティングします。テンプレートにないだけでサーバでもページルーティングは書けるので、それを使うことにしました。試しにサーバとクライアントで同名のルーティングを書くと、ブラウザで直接URLを書いた場合はサーバに行きました。但し、クライアント内で移動した場合はサーバに同名があってもサーバにいかずクライアントのままでルーティングされました。
Vueのrouterで"元URL+α"を元URLに書き戻す
別の言い方をするとサーバもクライアントも同名の状態ではサーバからリダイレクトしてクライアントには行けません。サーバから別名のクライアントへのリダイレクトは当然クライアントに行けます。そこでまたVueのrouterで"元URL+α"を元URLに書き戻すを参考にしました。クライアントに別名でリダイレクトして、さらにクライアントで同名に再リダイレクトすれば目的が達成できそうです。
自分のサイトでのリダイレクトの動き
自分のサイトで説明すると、ブラウザでhttp://manga-yonda.com/mangaにアクセスされた場合、まずサーバサイドに飛びます。そこですぐにjsでhttp://manga-yonda.com/_manga(mangaではなく、_manga)にリダイレクトします。するとこれはクライアントに行きます。クライアントの最初の処理でURLにアンダーバーが入っているかをチェックします。_mangaなので、チェックにかかり、ここで_を消したURLにリダイレクトします。クライアントのリダイレクトなので、晴れてクライアントのhttp://manga-yonda.com/mangaに行きます。
サーバの具体的なコード
つまり↑に書いた状態でサーバのページにOGPの設定をしれやれば、jsを解釈できる人間の操作の場合はクライアントに飛び、OGPクローラーの場合はjsを解釈出来ないのでサーバを見ます。具体的にはServerのコントローラーに以下のようなメソッドを用意します。
privateViewResultGetView(stringtitle,stringimage,stringurl,stringdescription){ViewData["OgTitle"]=title+"|マンガ読んだ!!";ViewData["OgImage"]=image;ViewData["OgUrl"]=$"https://{Request.Host.Value}/{url}";ViewData["OgDescription"]=description;ViewData["ReturnUrl"]=Request.Path.Value.Substring(1)+Request.QueryString.Value;returnView();}次にルーティングを書きます。
[HttpGet("manga")]publicIActionResultIndex()=>GetView("マンガ一覧",$"./content/manga.jpg","manga",$"マンガ読んだ!!のマンガページです");で、対応したViewを作ります。
<div><!-- このページのレイアウトはリダイレクトされるので自由。デバッグでも使えるようにOGPが読む情報の見える化した --><h1>OGP用ページ</h1><h2>すぐにリダイレクトされます。そのままお待ちください</h2><divclass="m-2"><imgclass="cover"src="@ViewData["OgImage"]"alt=''title="@ViewData["OgTitle"]"width="300"height="480"></div><div><div><h3class="font-weight-bold">@ViewData["OgTitle"]</h3></div><div>
@ViewData["OgDescription"]
</div></div><!-- OGPクローラーは以下は見ない --><script type="text/javascript">window.location="/_@ViewData["ReturnUrl"]";</script></div>共通の_Layout.cshtmlにもViewDataを記述します。これでページ毎の正しいOPGが設定できます。
<head><metacharset="utf-8"/><metaname="viewport"content="width=device-width, initial-scale=1.0"/><title>@ViewData["Title"]</title><!-- og系のpropertyが正しく設定されていればOGPクローラーはそれを見る --><metaproperty="og:url"content="@ViewData["OgUrl"]"><metaproperty="og:title"content="@ViewData["OgTitle"]"><metaproperty="og:description"content="@ViewData["OgDescription"]"><metaproperty="og:image"content="@ViewData["OgImage"]"><metaproperty="og:type"content="article"><metaproperty="og:site_name"content="マンガ読んだ!!"></head>クライアントの具体的なコード
クライアントのapp.razorには以下のようなメソッドを用意します。
@code{protectedoverridevoidOnInitialized(){//URLの中に_があった場合if(newUri(NavigationManager.Uri).AbsolutePath.Contains("_")){//_を外してHTMLデコードして投げるNavigationManager.NavigateTo(WebUtility.HtmlDecode(NavigationManager.Uri.Replace("_","")));}}}これで人間は目的としているページに飛ぶので、BlazorのOGP対策が完成です!マンガ読んだ!!はマンガのページをFacebookやTwitterに貼った場合、ちゃんとそのページのOGPが表示され、人間はSPAのページに移動します!
まとめ
BlazorでもOGPやSEOの簡単な対策は出来ました。OGPについてはBlazor Advent Calendar 2020の17日書かれたASP.NET Core Blazor WebAssembly のプリレンダリングを試してみようの手法でも対応が出来るかなと思います。ただ、SEOはGoogleの検索結果に出ているというだけで、必ずしも望んだ形では出ていないこともあり、ちゃんとやるならもっと深い技術が必要になってくるのかなと思います。また最近読んだNext.js 4年目の知見:SSRはもう古い、VercelにAPIサーバを置くなではSSR自体に対して意見を投げているので、こういった対応は今後変わってくるのかなとも思いました。
引用
SNS映えするWebアプリを...!FirebaseとVue.jsでSPAのOGP画像の動的生成をやってみたら案外楽だった
Next.js 4年目の知見:SSRはもう古い、VercelにAPIサーバを置くな
ASP.NET Core Blazor WebAssembly のプリレンダリングを試してみよう