1 Qu'est-ce qu'Action Cable ?
Action Cable intègre de manière transparente les WebSockets avec le reste de votre application Rails. Il permet d'écrire des fonctionnalités en temps réel en Ruby, dans le même style et la même forme que le reste de votre application Rails, tout en étant performant et scalable. C'est une offre complète qui fournit à la fois un framework JavaScript côté client et un framework Ruby côté serveur. Vous avez accès à l'ensemble de votre modèle de domaine écrit avec Active Record ou votre ORM de choix.
2 Terminologie
Action Cable utilise les WebSockets au lieu du protocole de requête-réponse HTTP. Tant Action Cable que les WebSockets introduisent une terminologie moins familière :
2.1 Connexions
Les connexions constituent la base de la relation client-serveur. Un seul serveur Action Cable peut gérer plusieurs instances de connexion. Il a une instance de connexion par connexion WebSocket. Un utilisateur unique peut avoir plusieurs WebSockets ouverts vers votre application s'il utilise plusieurs onglets de navigateur ou appareils.
2.2 Consommateurs
Le client d'une connexion WebSocket est appelé le consommateur. Dans Action Cable, le consommateur est créé par le framework JavaScript côté client.
2.3 Canaux
Chaque consommateur peut, à son tour, s'abonner à plusieurs canaux. Chaque canal encapsule une unité logique de travail, similaire à ce qu'un contrôleur fait dans une configuration MVC typique. Par exemple, vous pourriez avoir un ChatChannel
et un AppearancesChannel
, et un consommateur pourrait être abonné à l'un ou aux deux de ces canaux. Au minimum, un consommateur devrait être abonné à un canal.
2.4 Abonnés
Lorsque le consommateur est abonné à un canal, il agit en tant qu'abonné. La connexion entre l'abonné et le canal est, sans surprise, appelée une souscription. Un consommateur peut agir en tant qu'abonné à un canal donné un nombre illimité de fois. Par exemple, un consommateur pourrait s'abonner à plusieurs salles de discussion en même temps. (Et n'oubliez pas qu'un utilisateur physique peut avoir plusieurs consommateurs, un par onglet/appareil ouvert sur votre connexion).
2.5 Pub/Sub
Pub/Sub ou Publish-Subscribe fait référence à un paradigme de file d'attente de messages où les expéditeurs d'informations (éditeurs) envoient des données à une classe abstraite de destinataires (abonnés), sans spécifier les destinataires individuels. Action Cable utilise cette approche pour communiquer entre le serveur et de nombreux clients.
2.6 Diffusion
Une diffusion est un lien pub/sub où tout ce qui est transmis par le diffuseur est envoyé directement aux abonnés du canal qui diffusent cette diffusion nommée. Chaque canal peut diffuser zéro ou plusieurs diffusions.
3 Composants côté serveur
3.1 Connexions
Pour chaque WebSocket accepté par le serveur, un objet de connexion est instancié. Cet objet devient le parent de toutes les souscriptions de canal qui sont créées à partir de là. La connexion elle-même ne traite aucune logique d'application spécifique, en dehors de l'authentification et de l'autorisation. Le client d'une connexion WebSocket est appelé le consommateur de la connexion. Un utilisateur individuel créera une paire consommateur-connexion par onglet de navigateur, fenêtre ou appareil qu'il a ouvert.
Les connexions sont des instances de ApplicationCable::Connection
, qui étend ActionCable::Connection::Base
. Dans ApplicationCable::Connection
, vous autorisez la connexion entrante et procédez à son établissement si l'utilisateur peut être identifié.
3.1.1 Configuration de la connexion
# 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
Ici, identified_by
désigne un identifiant de connexion qui peut être utilisé pour retrouver la connexion spécifique ultérieurement. Notez que tout ce qui est marqué comme un identifiant créera automatiquement un délégué portant le même nom sur toutes les instances de canal créées à partir de la connexion.
Cet exemple repose sur le fait que vous avez déjà géré l'authentification de l'utilisateur ailleurs dans votre application, et qu'une authentification réussie définit un cookie chiffré avec l'ID de l'utilisateur.
Le cookie est ensuite automatiquement envoyé à l'instance de connexion lorsqu'une nouvelle connexion est tentée, et vous l'utilisez pour définir current_user
. En identifiant la connexion par ce même utilisateur actuel, vous vous assurez également que vous pouvez récupérer ultérieurement toutes les connexions ouvertes par un utilisateur donné (et éventuellement les déconnecter toutes si l'utilisateur est supprimé ou non autorisé).
Si votre approche d'authentification inclut l'utilisation d'une session, vous utilisez le cookie store pour la session, votre cookie de session est nommé _session
et la clé de l'ID utilisateur est user_id
, vous pouvez utiliser cette approche :
verified_user = User.find_by(id: cookies.encrypted['_session']['user_id'])
3.1.2 Gestion des exceptions
Par défaut, les exceptions non gérées sont capturées et enregistrées dans le journal de Rails. Si vous souhaitez intercepter ces exceptions de manière globale et les signaler à un service externe de suivi des bugs, par exemple, vous pouvez le faire avec rescue_from
:
# app/channels/application_cable/connection.rb
module ApplicationCable
class Connection < ActionCable::Connection::Base
rescue_from StandardError, with: :report_error
private
def report_error(e)
SomeExternalBugtrackingService.notify(e)
end
end
end
3.1.3 Rappels de connexion
Il existe des rappels before_command
, after_command
et around_command
disponibles pour être invoqués avant, après ou autour de chaque commande reçue par un client respectivement.
Le terme "commande" ici fait référence à toute interaction reçue par un client (abonnement, désabonnement ou exécution d'actions) :
# app/channels/application_cable/connection.rb
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :user
around_command :set_current_account
private
def set_current_account(&block)
# Maintenant, tous les canaux peuvent utiliser Current.account
Current.set(account: user.account, &block)
end
end
end
3.2 Canaux
Un canal encapsule une unité logique de travail, similaire à ce qu'un contrôleur fait dans une configuration MVC typique. Par défaut, Rails crée une classe parente ApplicationCable::Channel
(qui étend ActionCable::Channel::Base
) pour encapsuler la logique partagée entre vos canaux.
3.2.1 Configuration du canal parent
# app/channels/application_cable/channel.rb
module ApplicationCable
class Channel < ActionCable::Channel::Base
end
end
Ensuite, vous pouvez créer vos propres classes de canal. Par exemple, vous pourriez avoir un ChatChannel
et un AppearanceChannel
:
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
end
# app/channels/appearance_channel.rb
class AppearanceChannel < ApplicationCable::Channel
end
Un consommateur peut ensuite s'abonner à l'un ou aux deux de ces canaux.
3.2.2 Abonnements
Les consommateurs s'abonnent aux canaux, agissant en tant que abonnés. Leur connexion est appelée abonnement. Les messages produits sont ensuite routés vers ces abonnements de canal en fonction d'un identifiant envoyé par le consommateur de canal.
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
# Appelé lorsque le consommateur est devenu avec succès un abonné de ce canal.
def subscribed
end
end
3.2.3 Gestion des exceptions
Comme avec ApplicationCable::Connection
, vous pouvez également utiliser rescue_from
sur un canal spécifique pour gérer les exceptions levées :
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
rescue_from 'MyError', with: :deliver_error_message
private
def deliver_error_message(e)
broadcast_to(...)
end
end
3.2.4 Rappels de canal
ApplicationCable::Channel
fournit un certain nombre de rappels qui peuvent être utilisés pour déclencher une logique pendant le cycle de vie d'un canal. Les rappels disponibles sont :
before_subscribe
after_subscribe
(alias :on_subscribe
)before_unsubscribe
after_unsubscribe
(alias :on_unsubscribe
)
REMARQUE : Le rappel after_subscribe
est déclenché chaque fois que la méthode subscribed
est appelée, même si l'abonnement a été rejeté avec la méthode reject
. Pour déclencher after_subscribe
uniquement lors d'abonnements réussis, utilisez after_subscribe :send_welcome_message, unless: :subscription_rejected?
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
after_subscribe :send_welcome_message, unless: :subscription_rejected?
after_subscribe :track_subscription
private
def send_welcome_message
broadcast_to(...)
end
def track_subscription
# ...
end
end
4 Composants côté client
4.1 Connexions
Les consommateurs ont besoin d'une instance de la connexion de leur côté. Cela peut être établi en utilisant le JavaScript suivant, qui est généré par défaut par Rails :
4.1.1 Connecter le consommateur
// app/javascript/channels/consumer.js
// Action Cable fournit le framework pour gérer les WebSockets dans Rails.
// Vous pouvez générer de nouveaux canaux où les fonctionnalités WebSocket sont disponibles en utilisant la commande `bin/rails generate channel`.
import { createConsumer } from "@rails/actioncable"
export default createConsumer()
Cela préparera un consommateur qui se connectera par défaut à /cable
sur votre serveur. La connexion ne sera établie que lorsque vous aurez également spécifié au moins un abonnement qui vous intéresse.
Le consommateur peut éventuellement prendre un argument qui spécifie l'URL à laquelle se connecter. Cela peut être une chaîne de caractères ou une fonction qui renvoie une chaîne de caractères qui sera appelée lorsque le WebSocket est ouvert.
// Spécifier une URL différente à laquelle se connecter
createConsumer('wss://example.com/cable')
// Ou lors de l'utilisation de websockets sur HTTP
createConsumer('https://ws.example.com/cable')
// Utiliser une fonction pour générer dynamiquement l'URL
createConsumer(getWebSocketURL)
function getWebSocketURL() {
const token = localStorage.get('auth-token')
return `wss://example.com/cable?token=${token}`
}
4.1.2 Abonné
Un consommateur devient un abonné en créant un abonnement à un canal donné :
// app/javascript/channels/chat_channel.js
import consumer from "./consumer"
consumer.subscriptions.create({ channel: "ChatChannel", room: "Best Room" })
// app/javascript/channels/appearance_channel.js
import consumer from "./consumer"
consumer.subscriptions.create({ channel: "AppearanceChannel" })
Bien que cela crée l'abonnement, la fonctionnalité nécessaire pour répondre aux données reçues sera décrite ultérieurement. Un consommateur peut agir en tant qu'abonné à un canal donné un nombre illimité de fois. Par exemple, un consommateur peut s'abonner à plusieurs salles de discussion en même temps :
// app/javascript/channels/chat_channel.js
import consumer from "./consumer"
consumer.subscriptions.create({ channel: "ChatChannel", room: "1st Room" })
consumer.subscriptions.create({ channel: "ChatChannel", room: "2nd Room" })
5 Interactions client-serveur
5.1 Flux
Les flux fournissent le mécanisme par lequel les canaux routent le contenu publié (diffusions) vers leurs abonnés. Par exemple, le code suivant utilise stream_from
pour s'abonner à la diffusion nommée chat_Best Room
lorsque la valeur du paramètre :room
est "Best Room"
:
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat_#{params[:room]}"
end
end
Ensuite, ailleurs dans votre application Rails, vous pouvez diffuser vers une telle salle en appelant broadcast
:
ActionCable.server.broadcast("chat_Best Room", { body: "This Room is Best Room." })
Si vous avez un flux lié à un modèle, le nom de diffusion peut être généré à partir du canal et du modèle. Par exemple, le code suivant utilise stream_for
pour s'abonner à une diffusion comme posts:Z2lkOi8vVGVzdEFwcC9Qb3N0LzE
, où Z2lkOi8vVGVzdEFwcC9Qb3N0LzE
est le GlobalID du modèle Post.
class PostsChannel < ApplicationCable::Channel
def subscribed
post = Post.find(params[:id])
stream_for post
end
end
Vous pouvez ensuite diffuser vers ce canal en appelant broadcast_to
:
PostsChannel.broadcast_to(@post, @comment)
5.2 Diffusions
Une diffusion est un lien pub/sub où tout ce qui est transmis par un éditeur est routé directement vers les abonnés du canal qui diffusent cette diffusion nommée. Chaque canal peut diffuser zéro ou plusieurs diffusions.
Les diffusions sont purement une file d'attente en ligne et dépendent du temps. Si un consommateur ne diffuse pas (abonné à un canal donné), il ne recevra pas la diffusion s'il se connecte ultérieurement.
5.3 Abonnements
Lorsqu'un consommateur est abonné à un canal, il agit en tant qu'abonné. Cette connexion est appelée un abonnement. Les messages entrants sont ensuite routés vers ces abonnements de canal en fonction d'un identifiant envoyé par le consommateur de câble.
// app/javascript/channels/chat_channel.js
import consumer from "./consumer"
consumer.subscriptions.create({ channel: "ChatChannel", room: "Best Room" }, {
received(data) {
this.appendLine(data)
},
appendLine(data) {
const html = this.createLine(data)
const element = document.querySelector("[data-chat-room='Best Room']")
element.insertAdjacentHTML("beforeend", html)
},
createLine(data) {
return `
<article class="chat-line">
<span class="speaker">${data["sent_by"]}</span>
<span class="body">${data["body"]}</span>
</article>
`
}
})
5.4 Passage de paramètres aux canaux
Vous pouvez transmettre des paramètres du côté client au côté serveur lors de la création d'un abonnement. Par exemple :
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat_#{params[:room]}"
end
end
Un objet passé en tant que premier argument à subscriptions.create
devient le hachage de paramètres dans le canal de câble. Le mot-clé channel
est requis :
// app/javascript/channels/chat_channel.js
import consumer from "./consumer"
consumer.subscriptions.create({ channel: "ChatChannel", room: "Best Room" }, {
received(data) {
this.appendLine(data)
},
appendLine(data) {
const html = this.createLine(data)
const element = document.querySelector("[data-chat-room='Best Room']")
element.insertAdjacentHTML("beforeend", html)
},
createLine(data) {
return `
<article class="chat-line">
<span class="speaker">${data["sent_by"]}</span>
<span class="body">${data["body"]}</span>
</article>
`
}
})
# Quelque part dans votre application, cela est appelé, peut-être
# à partir d'un NewCommentJob.
ActionCable.server.broadcast(
"chat_#{room}",
{
sent_by: 'Paul',
body: 'This is a cool chat app.'
}
)
5.5 Rebroadcast d'un message
Un cas d'utilisation courant est de rebroadcast un message envoyé par un client à tous les autres clients connectés.
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat_#{params[:room]}"
end
def receive(data)
ActionCable.server.broadcast("chat_#{params[:room]}", data)
end
end
// app/javascript/channels/chat_channel.js
import consumer from "./consumer"
const chatChannel = consumer.subscriptions.create({ channel: "ChatChannel", room: "Best Room" }, {
received(data) {
// data => { sent_by: "Paul", body: "This is a cool chat app." }
}
}
chatChannel.send({ sent_by: "Paul", body: "This is a cool chat app." })
Le rebroadcast sera reçu par tous les clients connectés, y compris le client qui a envoyé le message. Notez que les paramètres sont les mêmes qu'au moment de l'abonnement au canal.
6 Exemples Full-Stack
Les étapes de configuration suivantes sont communes aux deux exemples :
6.1 Exemple 1 : Apparitions des utilisateurs
Voici un exemple simple d'un canal qui suit si un utilisateur est en ligne ou non et sur quelle page il se trouve. (Cela est utile pour créer des fonctionnalités de présence comme afficher un point vert à côté d'un nom d'utilisateur s'il est en ligne).
Créez le canal d'apparition côté serveur :
# app/channels/appearance_channel.rb
class AppearanceChannel < ApplicationCable::Channel
def subscribed
current_user.appear
end
def unsubscribed
current_user.disappear
end
def appear(data)
current_user.appear(on: data['appearing_on'])
end
def away
current_user.away
end
end
Lorsqu'un abonnement est initié, le rappel subscribed
est déclenché, et nous en profitons pour dire "l'utilisateur actuel est effectivement apparu". Cette API d'apparition/disparition peut être soutenue par Redis, une base de données, ou tout autre chose.
Créez l'abonnement du côté client au canal d'apparition :
// app/javascript/channels/appearance_channel.js
import consumer from "./consumer"
consumer.subscriptions.create("AppearanceChannel", {
// Appelé une fois lorsque l'abonnement est créé.
initialized() {
this.update = this.update.bind(this)
},
// Appelé lorsque l'abonnement est prêt à être utilisé sur le serveur.
connected() {
this.install()
this.update()
},
// Appelé lorsque la connexion WebSocket est fermée.
disconnected() {
this.uninstall()
},
// Appelé lorsque l'abonnement est rejeté par le serveur.
rejected() {
this.uninstall()
},
update() {
this.documentIsActive ? this.appear() : this.away()
},
appear() {
// Appelle `AppearanceChannel#appear(data)` sur le serveur.
this.perform("appear", { appearing_on: this.appearingOn })
},
away() {
// Appelle `AppearanceChannel#away` sur le serveur.
this.perform("away")
},
install() {
window.addEventListener("focus", this.update)
window.addEventListener("blur", this.update)
document.addEventListener("turbo:load", this.update)
document.addEventListener("visibilitychange", this.update)
},
uninstall() {
window.removeEventListener("focus", this.update)
window.removeEventListener("blur", this.update)
document.removeEventListener("turbo:load", this.update)
document.removeEventListener("visibilitychange", this.update)
},
get documentIsActive() {
return document.visibilityState === "visible" && document.hasFocus()
},
get appearingOn() {
const element = document.querySelector("[data-appearing-on]")
return element ? element.getAttribute("data-appearing-on") : null
}
})
6.1.1 Interaction Client-Serveur
Client se connecte au Serveur via
createConsumer()
. (consumer.js
). Le Serveur identifie cette connexion parcurrent_user
.Client s'abonne au canal d'apparition via
consumer.subscriptions.create({ channel: "AppearanceChannel" })
. (appearance_channel.js
)Serveur reconnaît qu'un nouvel abonnement a été initié pour le canal d'apparition et exécute son rappel
subscribed
, appelant la méthodeappear
surcurrent_user
. (appearance_channel.rb
)Client reconnaît qu'un abonnement a été établi et appelle
connected
(appearance_channel.js
), qui à son tour appelleinstall
etappear
.appear
appelleAppearanceChannel#appear(data)
sur le serveur, et fournit un hash de données{ appearing_on: this.appearingOn }
. Cela est possible car l'instance du canal côté serveur expose automatiquement toutes les méthodes publiques déclarées dans la classe (à l'exception des rappels), de sorte que celles-ci peuvent être atteintes en tant qu'appels de procédure à distance via la méthodeperform
d'un abonnement.Serveur reçoit la demande d'action
appear
sur le canal d'apparition pour la connexion identifiée parcurrent_user
(appearance_channel.rb
). Serveur récupère les données avec la clé:appearing_on
du hash de données et les définit comme valeur pour la clé:on
passée àcurrent_user.appear
.
6.2 Exemple 2 : Réception de nouvelles notifications Web
L'exemple d'apparition concernait l'exposition de fonctionnalités côté serveur à l'invocation côté client via la connexion WebSocket. Mais l'avantage des WebSockets est qu'il s'agit d'une voie à double sens. Ainsi, montrons maintenant un exemple où le serveur invoque une action côté client.
Il s'agit d'un canal de notification Web qui vous permet de déclencher des notifications Web côté client lorsque vous diffusez vers les flux pertinents :
Créez le canal de notification Web côté serveur :
# app/channels/web_notifications_channel.rb
class WebNotificationsChannel < ApplicationCable::Channel
def subscribed
stream_for current_user
end
end
Créez l'abonnement du canal de notification Web côté client :
// app/javascript/channels/web_notifications_channel.js
// Côté client, qui suppose que vous avez déjà demandé
// l'autorisation d'envoyer des notifications Web.
import consumer from "./consumer"
consumer.subscriptions.create("WebNotificationsChannel", {
received(data) {
new Notification(data["title"], { body: data["body"] })
}
})
Diffusez du contenu vers une instance de canal de notification Web à partir d'un autre endroit de votre application :
# Quelque part dans votre application, cela est appelé, peut-être depuis un NewCommentJob
WebNotificationsChannel.broadcast_to(
current_user,
title: 'Nouvelles choses !',
body: 'Toutes les nouvelles dignes d'être imprimées'
)
L'appel WebNotificationsChannel.broadcast_to
place un message dans la file d'attente pubsub de l'adaptateur d'abonnement actuel sous un nom de diffusion distinct pour chaque
utilisateur. Pour un utilisateur avec un ID de 1, le nom de diffusion serait
web_notifications:1
.
Le canal a été instruit de diffuser tout ce qui arrive à
web_notifications:1
directement vers le client en invoquant le rappel received
.
Les données transmises en argument sont le hash envoyé en tant que deuxième paramètre
à l'appel de diffusion côté serveur, encodé en JSON pour le trajet à travers le réseau
et déballé pour l'argument de données arrivant en tant que received
.
6.3 Exemples plus complets
Consultez le référentiel rails/actioncable-examples pour un exemple complet de configuration d'Action Cable dans une application Rails et d'ajout de canaux.
7 Configuration
Action Cable a deux configurations obligatoires : un adaptateur d'abonnement et des origines de requête autorisées.
7.1 Adaptateur d'abonnement
Par défaut, Action Cable recherche un fichier de configuration dans config/cable.yml
.
Le fichier doit spécifier un adaptateur pour chaque environnement Rails. Consultez la
section Dépendances pour plus d'informations sur les adaptateurs.
development:
adapter: async
test:
adapter: test
production:
adapter: redis
url: redis://10.10.3.153:6381
channel_prefix: appname_production
7.1.1 Configuration de l'adaptateur
Voici une liste des adaptateurs d'abonnement disponibles pour les utilisateurs finaux.
7.1.1.1 Adaptateur asynchrone
L'adaptateur asynchrone est destiné au développement/test et ne doit pas être utilisé en production.
7.1.1.2 Adaptateur Redis
L'adaptateur Redis nécessite que les utilisateurs fournissent une URL pointant vers le serveur Redis.
De plus, un channel_prefix
peut être fourni pour éviter les collisions de noms de canal
lors de l'utilisation du même serveur Redis pour plusieurs applications. Consultez la documentation Redis Pub/Sub pour plus de détails.
L'adaptateur Redis prend également en charge les connexions SSL/TLS. Les paramètres SSL/TLS requis peuvent être transmis dans la clé ssl_params
du fichier de configuration YAML.
production:
adapter: redis
url: rediss://10.10.3.153:tls_port
channel_prefix: appname_production
ssl_params: {
ca_file: "/path/to/ca.crt"
}
Les options données à ssl_params
sont transmises directement à la méthode OpenSSL::SSL::SSLContext#set_params
et peuvent être n'importe quel attribut valide du contexte SSL.
Veuillez vous référer à la documentation OpenSSL::SSL::SSLContext pour les autres attributs disponibles.
Si vous utilisez des certificats auto-signés pour l'adaptateur Redis derrière un pare-feu et que vous choisissez de sauter la vérification du certificat, alors le verify_mode
SSL doit être défini sur OpenSSL::SSL::VERIFY_NONE
.
AVERTISSEMENT: Il n'est pas recommandé d'utiliser VERIFY_NONE
en production à moins de comprendre parfaitement les implications en matière de sécurité. Pour définir cette option pour l'adaptateur Redis, la configuration doit être ssl_params: { verify_mode: <%= OpenSSL::SSL::VERIFY_NONE %> }
.
7.1.1.3 Adaptateur PostgreSQL
L'adaptateur PostgreSQL utilise le pool de connexions d'Active Record et donc la
configuration de la base de données config/database.yml
de l'application pour sa connexion.
Cela peut changer à l'avenir. #27214
7.2 Origines de requête autorisées
Action Cable n'acceptera que les requêtes provenant des origines spécifiées, qui sont transmises à la configuration du serveur sous forme de tableau. Les origines peuvent être des instances de chaînes de caractères ou des expressions régulières, contre lesquelles une vérification de correspondance sera effectuée.
config.action_cable.allowed_request_origins = ['https://rubyonrails.com', %r{http://ruby.*}]
Pour désactiver et autoriser les requêtes depuis n'importe quelle origine :
config.action_cable.disable_request_forgery_protection = true
Par défaut, Action Cable autorise toutes les requêtes depuis localhost:3000 lorsqu'il est exécuté en environnement de développement.
7.3 Configuration du consommateur
Pour configurer l'URL, ajoutez un appel à action_cable_meta_tag
dans votre mise en page HTML
HEAD. Cela utilise une URL ou un chemin généralement défini via config.action_cable.url
dans les
fichiers de configuration de l'environnement.
7.4 Configuration du pool de travailleurs
Le pool de travailleurs est utilisé pour exécuter les rappels de connexion et les actions de canal de manière isolée du thread principal du serveur. Action Cable permet à l'application de configurer le nombre de threads traités simultanément dans le pool de travailleurs.
config.action_cable.worker_pool_size = 4
Notez également que votre serveur doit fournir au moins le même nombre de connexions de base de données
que vous avez de travailleurs. La taille par défaut du pool de travailleurs est fixée à 4, donc
cela signifie que vous devez mettre au moins 4 connexions de base de données à disposition.
Vous pouvez modifier cela dans config/database.yml
via l'attribut pool
.
7.5 Journalisation côté client
La journalisation côté client est désactivée par défaut. Vous pouvez l'activer en définissant ActionCable.logger.enabled
sur true.
import * as ActionCable from '@rails/actioncable'
ActionCable.logger.enabled = true
7.6 Autres configurations
L'autre option courante à configurer est les balises de journalisation appliquées au journal spécifique à la connexion. Voici un exemple qui utilise l'ID du compte utilisateur si disponible, sinon "no-account" lors du marquage :
config.action_cable.log_tags = [
-> request { request.env['user_account_id'] || "no-account" },
:action_cable,
-> request { request.uuid }
]
Pour une liste complète de toutes les options de configuration, voir la
classe ActionCable::Server::Configuration
.
8 Exécution de serveurs Cable autonomes
Action Cable peut s'exécuter soit dans le cadre de votre application Rails, soit en tant que serveur autonome. En développement, l'exécution dans le cadre de votre application Rails est généralement suffisante, mais en production, vous devriez l'exécuter en tant que serveur autonome.
8.1 Dans l'application
Action Cable peut s'exécuter aux côtés de votre application Rails. Par exemple, pour
écouter les requêtes WebSocket sur /websocket
, spécifiez ce chemin à
config.action_cable.mount_path
:
# config/application.rb
class Application < Rails::Application
config.action_cable.mount_path = '/websocket'
end
Vous pouvez utiliser ActionCable.createConsumer()
pour vous connecter au serveur de câble
si action_cable_meta_tag
est invoqué dans la mise en page. Sinon, un chemin est
spécifié en premier argument à createConsumer
(par exemple, ActionCable.createConsumer("/websocket")
).
Pour chaque instance de votre serveur que vous créez, et pour chaque travailleur que votre serveur génère, vous aurez également une nouvelle instance d'Action Cable, mais l'adaptateur Redis ou PostgreSQL synchronise les messages entre les connexions.
8.2 Autonome
Les serveurs de câble peuvent être séparés de votre serveur d'application normal. C'est encore une application Rack, mais c'est sa propre application Rack. La configuration de base recommandée est la suivante : ```ruby
cable/config.ru
require_relative "../config/environment" Rails.application.eager_load!
run ActionCable.server ```
Ensuite, pour démarrer le serveur :
bundle exec puma -p 28080 cable/config.ru
Cela démarre un serveur de câble sur le port 28080. Pour indiquer à Rails d'utiliser ce serveur, mettez à jour votre configuration :
# config/environments/development.rb
Rails.application.configure do
config.action_cable.mount_path = nil
config.action_cable.url = "ws://localhost:28080" # utiliser wss:// en production
end
Enfin, assurez-vous d'avoir configuré correctement le consommateur.
8.3 Notes
Le serveur WebSocket n'a pas accès à la session, mais il a accès aux cookies. Cela peut être utilisé lorsque vous avez besoin de gérer l'authentification. Vous pouvez voir une façon de le faire avec Devise dans cet article.
9 Dépendances
Action Cable fournit une interface d'adaptateur d'abonnement pour traiter ses
internes de publication/abonnement. Par défaut, les adaptateurs asynchrones, en ligne, PostgreSQL et Redis
sont inclus. L'adaptateur par défaut
dans les nouvelles applications Rails est l'adaptateur asynchrone (async
).
Le côté Ruby des choses est construit sur websocket-driver, nio4r et concurrent-ruby.
10 Déploiement
Action Cable est alimenté par une combinaison de WebSockets et de threads. Tant le plomberie du framework que le travail de canal spécifié par l'utilisateur sont gérés en interne par utilisation du support natif des threads de Ruby. Cela signifie que vous pouvez utiliser tous vos modèles Rails existants sans problème, tant que vous n'avez pas commis de péchés de sécurité des threads.
Le serveur Action Cable implémente l'API de détournement de socket Rack, permettant ainsi l'utilisation d'un modèle multi-thread pour gérer les connexions en interne, indépendamment de la possibilité que le serveur d'application soit multi-thread ou non.
En conséquence, Action Cable fonctionne avec des serveurs populaires comme Unicorn, Puma et Passenger.
11 Test
Vous pouvez trouver des instructions détaillées sur la façon de tester votre fonctionnalité Action Cable dans le guide de test.
Retour d'information
Vous êtes encouragé à contribuer à l'amélioration de la qualité de ce guide.
Veuillez contribuer si vous trouvez des fautes de frappe ou des erreurs factuelles. Pour commencer, vous pouvez lire notre contribution à la documentation section.
Vous pouvez également trouver du contenu incomplet ou des informations qui ne sont pas à jour. Veuillez ajouter toute documentation manquante pour la version principale. Assurez-vous de vérifier Edge Guides d'abord pour vérifier si les problèmes ont déjà été résolus ou non sur la branche principale. Consultez les Directives des guides Ruby on Rails pour le style et les conventions.
Si pour une raison quelconque vous repérez quelque chose à corriger mais ne pouvez pas le faire vous-même, veuillez ouvrir un problème.
Et enfin, toute discussion concernant la documentation de Ruby on Rails est la bienvenue sur le Forum officiel de Ruby on Rails.