Rails动作电缆特定消费者

时间:2016-10-01 11:08:05

标签: ruby-on-rails actioncable

我正在努力使用动作电缆。在我的情况下,我有几个用户(通过Devise)可以相互分享任务。

现在,当user#1user#2共享任务(通过表单)时,所有经过身份验证的用户都会收到通知。

我应该如何以及在何处确定我的user#2仅向他广播?

到目前为止,这是我的代码:

connection.rb

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

    def connect
      self.current_user = find_verified_user
      logger.add_tags 'ActionCable', current_user.id
    end

    protected

    def find_verified_user # this checks whether a user is authenticated with devise
      if verified_user = env['warden'].user
        verified_user
      else
        reject_unauthorized_connection
      end
    end
  end
end

cable.js

(function() {
  this.App || (this.App = {});

  App.cable = ActionCable.createConsumer();

}).call(this);

todo_channel.rb

class TodoChannel < ApplicationCable::Channel
  def subscribed
    stream_from "todo_channel_#{current_user.id}"
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end


  def notify
    ActionCable.server.broadcast "todo_channel_#{current_user.id}", message: 'some message'(not implemented yet)
  end
end

todo.coffee

App.todo = App.cable.subscriptions.create "TodoChannel",
  connected: ->
    # Called when the subscription is ready for use on the server

  disconnected: ->
    # Called when the subscription has been terminated by the server

  received: (data) ->
    console.log(data['message'])

  notify: ->
    @perform 'notify'

4 个答案:

答案 0 :(得分:5)

我之前遇到过类似的事情,直到我意识到您可以在频道中多次拨打stream_from并且该用户将订阅多个不同的&#34;房间&#34;在同一频道连接中。这意味着你基本上可以做到这一点

class TodoChannel < ApplicationCable::Channel
  def subscribed
    stream_from "todo_channel_all"                            
    stream_from "todo_channel_#{current_user.id}"
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def notify(data)
    # depending on data given from the user, send it to only one specific user or everyone listening to the "todo_channel_all"
    if data['message']['other_user_id']
      ActionCable.server.broadcast "todo_channel_#{data['message']['other_user_id']}", message: 'some message'
    else
      ActionCable.server.broadcast "todo_channel_all", message: 'some message'
    end

  end
end

该代码假设用户已经知道其他用户的ID并将其发送到频道,您可能需要用一些安全性或其他东西来包装,我承认我没有很好的经验因为我还在学习。

将来可能对您有益的其他事实是您还可以在同一频道功能中广播多个消息/次。这意味着您可以支持将您的任务发送给单个特定用户,特定用户列表或每个人。只需迭代列表/数组/用户的任何内容,并在他们的个人&#34; todo_channel _#{user.id}&#34;上广播任务/消息/通知/任何内容。

答案 1 :(得分:3)

我最后采用了不同的方法。我会在这里写,以防有人发现它有用。

通知具有必须通知的用户的ID。所以在模型中我有:

def paraula(file,palabrabuscar):
    abrirLetra = open(file) #Open the file
    palabras = ""
    vegades = 0
    for palabras in abrirLetra.read().split():#I split all words in text
        if (palabras == palabrabuscar) :#Look for coincidence
            vegades = vegades +1

    abrirLetra.close()
    return vegades

file = 'lletra.txt'
print paraula(file,"you")

当我将广播放入模型时,after_commit :broadcast_notification, on: :create def broadcast_notification ActionCable.server.broadcast "todo_channel_#{self.user_id}", message: 'some message' end 看起来像这样:

todo_channel.rb

答案 2 :(得分:1)

步骤1:让每个用户拥有唯一的会话令牌。订阅时,每个用户都将在标头中发送会话令牌,并且可以在连接类中访问标头。使用会话令牌查找用户。

步骤2:在用户ID上串流用户。

步骤3:在共享任务时,在请求中也获取用户ID,并以给定的用户ID广播。

答案 3 :(得分:1)

这称为&#34;私人聊天&#34;。如果您真的想获得current_user.id,可以通过以下三种方式完成:

  1. onload到服务器的一些AJAX todo.coffee来电。

  2. 在Rails HTML视图中将current_user.id渲染为属性,然后通过todo.coffee内的jQuery获取(如https://www.sitepoint.com/create-a-chat-app-with-rails-5-actioncable-and-devise/中所示)

  3. 在用户登录时创建纯Cookie,然后在todo.coffee

  4. 中查看

    但你不应该使用current_user.id,因为它不安全。如果你使用它,那么有人可能会在同一个网站注册,并轻松听取其他用户的意见。私密聊天只需提供随机user_id

    而是使用签名(例如Rails加密)cookie作为用户的唯一广播标识符。这样,如果您在同一网站注册,您将永远不会知道其他用户的签名cookie - 因此您无法进入外来私人聊天。

    应用/配置/初始化/ warden_hooks.rb

    请参阅https://rubytutorial.io/actioncable-devise-authentication/

    # Be sure to restart your server when you modify this file.
    
    Warden::Manager.after_set_user do |user,auth,opts|
      scope = opts[:scope]
      auth.cookies.signed["#{scope}.id"] = user.id
    end
    
    Warden::Manager.before_logout do |user, auth, opts|
      scope = opts[:scope]
      auth.cookies.delete("#{scope}.id")
    end
    

    <强> todo_channel.rb

    class TodoChannel < ApplicationCable::Channel
      def subscribed
        stream_from "todo_channel_#{params['user_signed_cookie']}"
      end
    
      def unsubscribed
        # Any cleanup needed when channel is unsubscribed
      end
    
    
      def notify(param_message)
        ActionCable.server.broadcast "todo_channel_#{param_message['user_signed_cookie']}", message: 'some message'(not implemented yet)
      end
    end
    

    <强> todo.coffee

    user_signed_cookie = document.cookie.replace(/(?:(?:^|.*;\s*)user.id\s*\=\s*([^;]*).*$)|^.*$/, "$1");
    
    user_logged_in = if user_signed_cookie then true else false
    
    if user_logged_in #avoid repetitive HTTP->WebSockets upgrade pointless browser attempts when no user logged in.
     App.todo = App.cable.subscriptions.create {
              channel:"TodoChannel"
              user_signed_cookie: user_signed_cookie
            },
      connected: ->
        # Called when the subscription is ready for use on the server
    
      disconnected: ->
        # Called when the subscription has been terminated by the server
    
      received: (data) ->
        console.log(data['message'])
    
      notify: (name, content, _) -> #name, content - message fields
        @perform 'notify', name: name, content: content, user_signed_cookie: user_signed_cookie