更新情報

みなさん、こんにちは。

本コラムでは、何かと環境面の話題が多めでしたが、今回はアプリケーションの機能に着目した内容をご紹介したいと思います。

昨今、Webアプリケーションの形態は、SPA(Single Page Application)というタイプのものが大半を占めていることは皆さんもご存知かと思います。SPAでは、最初のページにあたるHTMLのみをサーバから取得し、その後の画面の書き換えやプレゼンテーションロジックをすべてクライアント側(ブラウザ内)のJavaScriptで実施します。そのため、SPAのアプリケーションでは、サーバ側のアプリケーションにはプレゼンテーションロジックは必要なく、いわゆるWebAPIとしてブラウザ側のJavaScriptとAjax通信を行うのみというケースも多くなります。

Railsアプリからプレゼンテーションロジックが不要になると、Railsの機能の活用の幅が少し小さくなるので、ちょっと残念なところもありますが、今回はRails側でWebAPI的な応答を返す方法についてご紹介しましょう。

なお、Rails5からは、WebAPIに特化したアプリを構築するための機能(API mode)が追加されておりますが、今回は基本知識も再確認いただく意味で、通常のWebアプリケーションにWebAPI機能を加える方法としてご覧いただきます。

Rails5のAPI modeについて詳しくは、以下サイトをご覧ください。

Rails による API 専用アプリ | Railsガイド

https://railsguides.jp/api_app.html

さて、通常、Railsのコントローラクラス内では、必要なビジネスロジックを実行したのちに、ビューの制御を行います。コントローラのアクションメソッドで、respond_toメソッドを利用すれば、リクエスト時のフォーマット指定に応じたビューの挙動を設定できることは皆さんご存知の通りかと思います。

以下は、scaffoldで生成した典型的なメソッドの例です。

 

def index

@items = Item.all

 

respond_to do |format|

format.html # index.html.erb

format.json { render json: @items }

end

end

リクエストでフォーマットの指定がHTMLだった場合(デフォルト)は、Railsの規約に従い、アクションメソッド名と同名の拡張子「.html.erb」ファイルがビューとしてレンダリングの対象になります。もし、リクエストのフォーマット指定がJSON(URLに.jsonを付与)の場合は、format.jsonメソッドのブロック内の処理であるrenderメソッドのjson:オプションに従って、モデル@itemsの内容をJSONで記述したものがレスポンスとして返されます。

まあ、ここまででしたら、このコラムを読まれている方ならみなさんご存知かもしれないですね。ところで、JSON形式のレスポンスが求められるのはどのようなケースだったでしょうか。

冒頭にも触れましたが、JSON形式でデータを扱いたいのはWebAPI的な用途であることが多いです。クライアントアプリケーション(JavaScriptなど)とサーバサイドアプリケーション(WebAPI)が同一ドメインであれば、上記のような実装で問題ないのですが、WebAPIの場合、クライアントアプリケーションはサーバサイドアプリケーションと異なるドメインからのアクセスであることも考えられます。Webブラウザ上のJavaScriptの実行に関しては「クロスドメイン制約」というものがあるため、原則として、そのまま素直に別ドメインに属するサーバにリクエストを発行することはできないことになっています。

これを回避するために、JavaScriptではJSONPというテクニックを利用します。

JSONP自体の詳細な説明はここでは省きますが、考え方は以下のようなものです。

普通のJavaScriptコードでは別ドメインにリクエストを発行することはできないのですが、<script>タグのsrc属性では別ドメインにあるJavaScriptファイルを取得することができ、かつそのファイル内の内容がロード後に実行されます。JSONPはその仕組みを活用して、別ドメインから間接的にデータを取得することができるようになっています。

このとき、サーバ側の実装で問題となるのが、単にJSONドキュメントをレスポンスとすればよいのではなく、「コールバック関数呼び出し」の形でレスポンスを返却しなければいけない点です。

なおかつ、コールバック関数の名称は通常、クライアント側(JavaScript)で指定されますので、サーバ側(Rails)でその名称に合わせてコールバック関数を組み立てなくてはいけません。

Railsでは、renderメソッドで:jsonオプションを付けた際には、:callbackオプションによってコールバック関数名を指定すると、自動でJSONP形式のレスポンスが返却できるようになっています。

おそらく、多くの場合、コールバック関数名を示すリクエストパラメータは「callback」が多いと思われますので、params[:callback]メソッドで受け取った値を、:callbackオプションに渡します(実際には、セキュリティ的な問題が生じないようなパラメータ内容のチェックなどがあると望ましいです)。

def index

@items = Item.all

 

respond_to do |format|

format.html # index.html.erb

format.json { render json: @items, callback: params[:callback] }

end

end

もし、コントローラ内のほとんどのメソッドでJSONP対応が必要な場合は、ApplicationControllerなどの基底クラスに、共通メソッドを作成するなどの工夫をするのもよいですね。

いかがでしたでしょうか。RailsはMVCフレームワークですが、Viewを持たないWebAPI的なアプリケーションにも簡単に対応でき、かつViewの切り替え手順が柔軟なことで、旧来のWebアプリケーションとWebAPIの両方に一つのアプリで対応することもできるのですね。

以上