wscatでAction Cableと通信する
Railsで/cableなどのエンドポイントにAction CableをマウントするとWebSocketサーバとして利用できます。wscatを使ってAction CableによるWebSocket APIと対話的に通信するために、送信するデータの形式などを調べました。
準備
wscat
npmでインストールできます。
$ npm install -g wscatAction Cable
この記事ではRails 5.1.6を使います。今回は、APIモードのRailsアプリケーションにAction Cableをマウントします(Action Cableサーバを独立に起動することも可能)。まず、適当にアプリを作ります。
$ rails new --api action-cable-sample$ cd action-cable-sample$ bin/rails g scaffold message body:string$ bin/rails db:migrateconfig/application.rbでaction_cable/engineを読み込み、さらにマウントパスを指定します。/cableにマウントするのがRails wayの様子なのでそうします。
require_relative 'boot'
require "rails"# ...require "action_cable/engine"# ...
module ActionCableSample class Application < Rails::Application # ... config.api_only = true
config.action_cable.mount_path = '/cable' endendまた、デフォルトではCSRF対策で同じオリジンからしかWebSocket通信できないので、開発環境ではどのオリジンからでもWebSocket通信できるように設定します。
Rails.application.configure do # ... config.action_cable.disable_request_forgery_protection = trueendあとはAction Cableのチャンネルを適当に作ります。
$ bin/rails g channel messageMessageChannelは次のように書いておきます。
class MessageChannel < ApplicationCable::Channel def subscribed stream_from 'message_channel' end
def unsubscribed endendまた、今回はmessages#createが成功したときにWebSocket経由でメッセージをブロードキャストします。
class MessagesController < ApplicationController # ...
def create @message = Message.new(message_params)
if @message.save ActionCable.server.broadcast 'message_channel', body: @message.body
render json: @message, status: :created, location: @message else render json: @message.errors, status: :unprocessable_entity end endend実際に通信する
次のコマンドでWebSocketサーバへ接続します。HTTPでリクエストしてからWebSocketへアップグレードする処理などは自動でやってくれます。
$ wscat -c localhost:3000/cable
connected (press CTRL+C to quit)
< {"type":"welcome"}>Action Cableへ送信するデータにはsubscribe, message, unsubscribeの3種類があります。次の形式でデータを送信することでAction Cableとやりとりできます。
{"command":"subscribe","identifier":"{\"channel\":\"MessageChannel\"}"}{"command":"message","identifier":"{\"channel\":\"MessageChannel\"","data":"\"action\":\"chat\"}"} # "chat"は例です{"command":"unsubscribe","identifier":"{\"channel\":\"MessageChannel\"}"}Action CableはフルスタックアプリでJSを書いて使うことを想定されているためか、このあたりの仕様はREADMEやRails Guidesを見てもとくにドキュメント化されていないようでした。仕様を把握するにはAction Cableのコードを読む必要があります。
ActionCable::Connection::Subscriptions#execute_commandで受信したデータを解析し、commandに指定された文字列subscribe, message, unsubscribeによって処理を分岐しています。messageを送信したときはActionCable::Channel::Base#perform_actionに移り、受信データのactionで指定された名前を持つチャンネルのメソッドを動的に呼び出しています。
また、キー"identifier"の値が文字列化されたJSONになっているのは、この文字列がActionCable::Connection::Subscriptionsの中でActiveSupport::JSON.decodeに渡るからです。
実際に上述した形式のsubscribeのデータを送ると、チャンネルを購読できます。
> {"command":"subscribe","identifier":"{\"channel\":\"MessageChannel\"}"}< {"identifier":"{\"channel\":\"MessageChannel\"}","type":"confirm_subscription"}その後、コントローラのアクション内からブロードキャストするとメッセージを受信できます。
# curlで叩く$ curl --request POST --url http://localhost:3000/messages --header 'content-type: application/json' --data '{"message":{"body":"test"}}'
# wscatでデータを受信する< {"identifier":"{\"channel\":\"MessageChannel\"}","message":{"body":"test"}}