简体   繁体   中英

Rails' ActionCable server-side subscribe with params

I'm building a REST API using Ruby on Rails as a backend. This API is consumed by a VueJS frontend. I am not using Rails' frontend, the frontend is completely independent.

I have established a WebSocket connection using ActionCable but I'm struggling to send params to the subscription.

I have the following ruby channel :

class WorkChannel < ApplicationCable::Channel
    def subscribed
        stream_from "works-#{params[:creator_id]}"
    end
end

And in my JavaScript frontend, I subscribe to the channel using this code :

const msg = {
    command: 'subscribe',
    identifier: JSON.stringify({
        channel: 'WorkChannel',
    }),
};
socket.send(JSON.stringify(msg));

The subscription is successful if I remove the variable in the stream_from part.

On all guides and tutorials that I've read, I only found one explaining how to communicate with ActionCable without using Rails' frontend methods. See here for the complete guide.

However, this guide does not send data to the subscription and I can't find it on the documentation.

What is the JSON syntax of the object I need to send to emulate the behavior of App.cable.subscriptions.create({channel: 'WorkChannel', creator_id: 1 } ?

I tried different syntaxes but none of them was working :

const msg = {
    command: 'subscribe',
    identifier: JSON.stringify({
        channel: 'WorkChannel',
    }),
    creator_id: 1,
};

Or

const msg = {
    command: 'subscribe',
    identifier: JSON.stringify({
        channel: 'WorkChannel',
        creator_id: 1,
    }),  
};

And even

const msg = {
    command: 'subscribe',
    identifier: JSON.stringify({
        channel: 'WorkChannel',
    }),
    data: {
        creator_id: 1,
    },
};

You don't want to send the user id as a parameter, it can be exploited. Imagine a user pauses the js excecution, changes the user_id and resumes, he will receive messages that goes to other users.

I'd recommend you try to get the current user id from the logged user, something like https://guides.rubyonrails.org/action_cable_overview.html#server-side-components

# app/channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    private
      def find_verified_user
        if verified_user = User.find_by(id: cookies.encrypted[:user_id])
          verified_user
        else
          reject_unauthorized_connection
        end
      end
  end
end

The correct current user detection may change depending on what authentication system you use, if you use devise, you may need to get the user from warden like this: https://stackoverflow.com/a/38702351/1430810

This is the exact syntax you should use:

{
    "command": "subscribe",
    "identifier": "{\"channel\": \"WorkChannel\", \"other_params\": \"other_stuff\",}",
    "data": 
        "{\"action\": \"some_method\", \"args\": \"{\\\"arg_1\\\": 1}\"}"
}

Few things to note:

  • As you can see it's a JSON object (hence the \\ ), normal Hash will not work.
  • The channel param should be the channel class in CamelCase.
  • By using the data object you can directly call some methods you defined in the channel class (with args).

If you are using JSON Tokens (JWT) for authentication in you Rails API, you can simply send the token in the params and access it in the connection method.

# app/channels/application_cable/connection.rb

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    private

    def find_verified_user
      # here you can handle authentication using the token
      # to access the token: request.params[:user_token]
    end
  end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM