
Partager:
Phil is Head of Developer Relations at Hookdeck, an asynchronous messaging platform, and a proud Vonage alumni.
Passer des appels téléphoniques depuis un navigateur Web avec Vue.js et Vonage
Temps de lecture : 19 minutes
Dans cet article de blog, nous verrons comment vous pouvez passer un appel téléphonique à partir d'un navigateur Web vers un téléphone avec In-App Voice de Vonage en utilisant le Client SDK de Vonage pour JavaScript et Vue.JS. In-App Voice et In-App Messaging sont en avant-première pour les développeurs et nous aimerions recevoir vos commentaires sur l'expérience de développement que vous avez et sur la fonctionnalité qui est fournie. Vous pouvez nous contacter via Communauté des développeurs de Vonage Slack.
Pour passer un appel téléphonique depuis un navigateur web, nous allons avoir besoin d'un certain nombre de composants dans notre application. Une application Vue.JS qui s'exécute dans le navigateur et qui utilise le Vonage Client SDK pour JavaScript, un serveur d'application utilisé pour authentifier l'utilisateur de l'application auprès de Vonage en générant un JWT d'utilisateur, et un téléphone pour recevoir l'appel téléphonique.
Le diagramme de séquence ci-dessous montre comment les choses fonctionneront une fois que nous aurons construit notre application. Dans cet article de blog, nous allons d'abord créer l'application Vue.JS avec une interface utilisateur qui permet de saisir un numéro de téléphone. Nous créerons ensuite un serveur d'application capable de générer le JWT utilisateur requis. Une fois le serveur d'application opérationnel, nous mettrons à jour l'application Vue.JS pour récupérer le JWT et l'utiliser avec le Client SDK Vonage Client SDK pour JavaScript afin de se connecter à la plateforme Vonage et d'initier l'appel téléphonique. Nous devons ensuite mettre à jour le serveur de l'application pour gérer une requête GET que Vonage effectuera afin de récupérer les instructions sur la manière de procéder à l'appel téléphonique. Ces instructions indiqueront à Vonage de connecter l'appel depuis l'application Vue.JS dans le navigateur web vers un téléphone.
/* https://bramp.github.io/js-sequence-diagrams/ Participant Vue.JS App en tant que V Participant App Server en tant que A Participant Nexmo en tant que N Participant Phone en tant que P V->A : Get User JWT Note à droite de V : Dans une application de production<br/>cette demande devrait<br/>être authentifiée A-->V : User JWT V -> V : Create Nexmo<br/>Conversation Client V -> N : Login N --> V : Logged In V -> N : Call Phone N -> A : GET answer_url A --> N : NCCO connect N -> P : Call */
Call from Web Browser Sequence Diagram
Il y a donc quelques étapes à franchir, mais le résultat en vaut la peine.
Si vous préférez vous plonger directement dans le code, vous pouvez trouver le code suivant Code de l'appel depuis le navigateur sur GitHub.
Avant de commencer
Yarn pour la gestion des paquets
L'interface CLI de Vue pour l'échafaudage de notre application et l'exécution d'un serveur de développement
A Compte Nexmo pour nous permettre d'utiliser le SDK et de passer des appels téléphoniques
La Nexmo CLI pour créer et configurer rapidement une application Nexmo à partir de la ligne de commande. Veuillez utiliser la version beta du CLI, par exemple
npm install -g nexmo@betaUne solution de tunnel local telle que Ngrok afin que la plateforme Nexmo puisse accéder à un serveur web local). Pour ce billet de blog, nous allons utiliser Ngrok.
Une fois ces éléments en place, commençons.
Echafaudage d'un nouveau projet Vue.JS
Exécutez la commande suivante vue dans le terminal et choisissez la valeur par défaut de (babel, eslint) à l'invite.
$ vue create call-from-browser
# navigate into the newly created Vue project folder
cd call-from-browserVous obtiendrez alors la structure de répertoire et les fichiers suivants :
call-from-browser
├── README.md
├── babel.config.js
├── package.json
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── HelloWorld.vue
│ └── main.js
└── yarn.lockNous allons construire un composant CallFromBrowser donc renommons le composant par défaut HelloWorld qui a été créé.
Nous sommes maintenant prêts à construire l'interface utilisateur.
Créer une interface simple de saisie et de composition de numéros de téléphone
Vue.JS dispose d'un écosystème solide et en pleine croissance, il est donc logique d'utiliser les composants existants s'il y en a. Heureusement, il y a quelques options et nous allons choisir le composant composant vue-tel-input par Steven Dao.
vue-tel-input example animation
Installer le composant :
Maintenant que nous avons le composant qui nous aide à valider les numéros de téléphone, nous pouvons l'ajouter au composant CallFromBrowser au composant de l'application. Ouvrez src/components/CallFromBrowser.vue dans un éditeur de code.
Mettez à jour l'élément <template> comme suit :
<template>
<main class="call-from-browser">
<vue-tel-input @oninput="onInput">
</vue-tel-input>
<button class="call-control" v-bind:class="{'call-in-progress': callInProgress}" v-on:click="controlCallClick"></button>
<p>{{infoMessage}}</p>
</main>
</template>Le modèle utilise le composant <vue-tel-input> et définit un @onInput gestionnaire. Nous avons un <button> qui aura une classe call-in-progress en fonction de la valeur d'une propriété callInProgress et un gestionnaire de clic qui appellera une méthode controlCallClick méthode. Le modèle dispose également d'un <p>{{infoMessage}}</p> qui nous permet de fournir un retour d'information à l'utilisateur par l'intermédiaire d'une propriété data.infoMessage que nous allons définir.
Mettons ensuite à jour le contenu de la balise <script> dans le même fichier.
<script>
import 'vue-tel-input/dist/vue-tel-input.css'
import VueTelInput from 'vue-tel-input'
export default {
name: 'CallFromBrowser',
components: {
'vue-tel-input': VueTelInput
},
...
</code></pre>
<p>This imports the CSS and the component definition for the telephone input component sets the name of the component to <code>CallFromBrowser</code> and registers the <code>vue-tel-input</code> component dependency so it can be used within the template.</p>
<p>Next let's set up some properties for data binding such as the <code>callInProgress</code> property relied upon by the <code>template</code> and add any methods that are expected to be in place, as show, in the template:</p>
<pre><code class="language-javascript">export default {
name: 'CallFromBrowser',
components: {
'vue-tel-input': VueTelInput
},
data() {
return {
phone: {
number: '',
isValid: false,
country: {}
},
infoMessage: "",
callInProgress: false
}
},
methods: {
onInput({ number, isValid, country }) {
this.phone.number = number;
this.phone.isValid = isValid;
this.phone.country = country;
if(!isValid) {
this.infoMessage = "Please enter a valid phone number"
}
else {
this.infoMessage = `Thanks for entering a valid ${this.phone.country.name} phone number`
}
},
controlCallClick() {
}
}
}
</script>La fonction data renvoie un objet phone que nous remplissons dans le onInput handler. Dans ce gestionnaire, nous définissons le numéro de téléphone que l'utilisateur a saisi dans le composant vue-tel-input et les propriétés représentant la validité du numéro de téléphone et le pays pour lequel le numéro est utilisé.
Nous fournissons également à l'utilisateur un retour d'information sur la validité du numéro de téléphone en définissant la propriété infoMessage . La liaison de données Vue signifie que la valeur que nous définissons ici est reflétée dans l'interface utilisateur.
Une méthode controlCallClick est également ajoutée pour gérer le clic sur l'icône <button> qui est cliqué.
La dernière étape de la configuration du composant CallFromBrowser est d'ajouter un peu de style. Remplacez l'élément <style> existant et son contenu par ce qui suit :
<style scoped="">
.vue-tel-input {
width: 200px;
margin: auto;
}
.call-control {
font-size: 11em;
}
.call-control:before {
content: '☎️';
}
.call-control.call-in-progress:before {
content: '?'
}
</style>Le style définit la valeur par défaut content par défaut de la <button> à l'emoji téléphone rouge (☎️). Si la classe call-in-progress est présente, elle est définie dynamiquement si la propriété callInProgress renvoie la propriété true alors l'emoji content sera à la place un emoji de combiné téléphonique de style ancien ( ?).
La dernière étape de la mise en place de l'interface utilisateur de base consiste à mettre à jour App.vue en remplaçant les éléments template et script et les balises Laissez la balise style telle quelle.
<template>
<div id="app">
<callfrombrowser>
</callfrombrowser></div>
</template>
<script>
import CallFromBrowser from './components/CallFromBrowser.vue'
export default {
name: 'app',
components: {
CallFromBrowser
}
}
</script>Remplacer <template> l'importation de la définition du composant et l'enregistrement du composant importé. CallFromBrowser.vue et enregistrer le composant importé.
Nous pouvons maintenant exécuter l'application :
Une fois cette opération effectuée, il suffit de naviguer dans un navigateur jusqu'à l'adresse http://localhost:8080 et essayez d'entrer des numéros de téléphone dans le composant vue-input-tel dans le composant. Le numéro de téléphone sera validé au bas de l'interface utilisateur de l'application.
Call from Browser simple user interface
Création d'un JWT d'utilisateur pour se connecter à la plateforme Vonage
Le SDK JavaScript Nexmo Stitch se connecte à la plateforme Voice pour activer la fonctionnalité In-App Voice dans le navigateur web. Pour se connecter à la plateforme Nexmo, il faut login avec un JWT (JSON Web Token) d'authentification utilisateur valide pour l'utilisateur de l'application qui définit les permissions de cet utilisateur. Pour créer un JWT d'utilisateur, nous allons devoir créer quelques éléments :
un simple serveur qui génère le JWT de l'utilisateur qui peut être récupéré par le composant
CallFromBrowsercomposant Vue.JSune Applications au sein de la plateforme Vonage - nous pouvons le faire en utilisant le CLI Nexmo
un utilisateur au sein de l'Applications pour les utilisateurs actuels de l'application web
Commençons par créer un simple serveur. Créez un répertoire server installez quelques dépendances et créez un répertoire index.js et .env pour les fonctionnalités dont nous avons besoin.
Pour le serveur, nous allons utiliser Express.js avec les intergiciels CORS et body-parser. dotenv est utilisé pour charger le fichier .env qui contiendra lui-même la configuration que nous ne voulons pas dans le contrôle de source. Nous avons également installé la bibliothèque Nexmo Node.JS library pour aider à la génération de JWT pour l'utilisateur.
Avant d'examiner le code du serveur, créons également l'Application et l'Utilisateur pour cette application. Nous pouvons le faire en utilisant le CLI Nexmo :
L'exécution de cette commande permet d'obtenir un numéro d'identification de l'application. Elle ajoutera également les détails de l'Application à un fichier .nexmo-app fichier. Prenez l'identifiant de l'application et ajoutez-le au fichier .env ainsi qu'une variable pour l'emplacement private.key l'emplacement :
NEXMO_PRIVATE_KEY=private.key
NEXMO_APP_ID=YOUR_APPLICATION_IDLe dernier élément de la configuration de l'Application est la création d'un utilisateur dans l'application. Il est possible de le faire en utilisant les bibliothèques Nexmo, mais dans ce cas, nous allons créer un utilisateur en utilisant le CLI Nexmo :
Cette commande créera l'utilisateur pour l'ID de l'application identifiée dans le fichier .nexmo-app dans le fichier Ajoutez une variable d'environnement pour le nom de l'utilisateur dans le fichier .env pour le nom de l'utilisateur.
NEXMO_PRIVATE_KEY=private.key
NEXMO_APP_ID=YOUR_APPLICATION_ID
NEXMO_APP_USER_NAME=demoOuvrez maintenant index.js pour ajouter le code du serveur de base :
// Load .env config
require('dotenv').config({
path: __dirname + '/.env'
});
const Nexmo = require('nexmo')
const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const app = express()
app.use(bodyParser.json())
app.use(cors())
// endpoint that doesn't authenticate the user
// it will simply return a JWT with every request
app.get('/no-auth', (req, res) => {
res.json({userJwt: null})
})
app.listen(3000, () => console.log('Example app listening on port 3000!'))Dans le code ci-dessus, nous chargeons Express et le configurons pour qu'il analyse les requêtes entrantes au format JSON (nous l'utiliserons plus tard). Nous configurons également Express pour qu'il prenne en charge le partage des ressources entre origines (CORS). Cela est nécessaire car l'application Vue.JS s'exécute sur localhost:8080 et le code JavaScript qui s'exécute dans le navigateur doit appeler ce serveur qui s'exécute sur localhost:3000un port différent.
Vous pouvez maintenant lancer node index.js puis accéder à http://localhost:3000/no-auth pour vous assurer que le point d'accès renvoie le JSON attendu.
User JWT with null value
Maintenant, ajoutons le code pour générer le JWT de l'utilisateur à utiliser avec le Vonage Client SDK pour JavaScript.
const userAcl = {
"paths": {
"/v1/users/**": {},
"/v1/conversations/**": {},
"/v1/sessions/**": {},
"/v1/knocking/**": {}
}
}
// endpoint that doesn't authenticate the user
// it will simply return a JWT with every request
app.get('/no-auth', (req, res) => {
const jwt = Nexmo.generateJwt(process.env.NEXMO_PRIVATE_KEY, {
application_id: process.env.NEXMO_APP_ID,
sub: process.env.NEXMO_APP_USER_NAME,
exp: new Date().getTime() + 86400,
acl: userAcl
})
res.json({userJwt: jwt})
})Les variables userAcl fournissent un ensemble de revendications ou de règles d'accès qui sont utilisées lors de la création du JWT, ainsi que l'identifiant de l'application, une balise sub pour le nom de l'utilisateur et un exp comme délai d'expiration du JWT. Voir la page Vue d'ensemble des JWT et ACL dans Nexmo Developer pour plus d'informations.
Redémarrage du nœud index.js et en accédant à http://localhost:3000/no-auth montrera qu'un véritable JWT a été généré.
User JWT with real JWT value
Note : Il peut parfois être utile de jeter un coup d'œil à Débogueur JWT pour vérifier le contenu de votre JWT.
Récupérer le JWT de l'utilisateur à partir du navigateur Web
La génération de JWT étant en place, nous pouvons revenir au client pour récupérer le JWT de l'utilisateur que nous avons créé sur le serveur.
Étant donné qu'il ne serait pas judicieux de coder à 100 % l'URL du serveur, nous la rendrons paramétrable via une propriété Vue.JS avec une valeur par défaut qui est pratique pour notre configuration de développement.
...
callInProgress: false
}
},
props: {
jwtUrl: {
type: String,
default: process.env.VUE_APP_JWT_URL || "http://localhost:3000/no-auth"
}
},La valeur jwtUrl peut alors être remplacée par la définition d'une propriété jwt-url sur l'élément <CallFromBrowser> et l'élément default peut être modifiée lors de la construction du composant en définissant une valeur de VUE_APP_JWT_URL dans un fichier .env dans notre répertoire de premier niveau. Pour plus d'informations, voir Vue.JS props et Vue CLI 3 Variables d'environnement et modes.
L'URL du serveur étant définie, nous pouvons maintenant fetch le JWT de l'utilisateur. Vue.JS dispose de plusieurs crochets de cycle de vie. Nous allons récupérer le JWT dans le hook created . Pour ce faire, nous définissons une fonction created dans la définition de CallFromBrowser définition.
props: {
jwtUrl: {
type: String,
default: process.env.VUE_APP_JWT_URL || "http://localhost:3000/no-auth"
}
},
created() {
fetch(this.$props.jwtUrl)
.then(response => {
return response.json();
})
.then(json => {
console.log(json)
})
.catch(error => {
console.error(error)
})
},Assurez-vous que votre serveur de développement Vue.JS est toujours en cours d'exécution (exécutez yarn serve à partir du répertoire call-from-browser si ce n'est pas le cas), naviguez vers http://localhost:8080 et ouvrez vos outils de développement pour vérifier la console et vous assurer que le JWT de l'utilisateur est enregistré.
JWT now present in browser and output via console.log
Ajout du Client SDK de Vonage pour JavaScript
Avec le JWT de l'utilisateur dans le client, nous pouvons inclure le Client SDK de Vonage pour JavaScript :
Ensuite, incluez le SDK dans le composant CallFromBrowser.vue et importez la définition de l'objet ConversationClient la définition de l'objet :
<script>
import 'vue-tel-input/dist/vue-tel-input.css'
import VueTelInput from 'vue-tel-input'
import ConversationClient from 'nexmo-stitch'Une fois que nous avons inclus la définition requise, nous devons créer une nouvelle instance de l'élément ConversationClient et login. Nous le ferons après avoir récupéré le JWT de l'utilisateur :
created() {
fetch(this.$props.jwtUrl)
.then(response => {
return response.json();
})
.then(json => {
this.conversationClient = new ConversationClient({debug: true})
return this.conversationClient.login(json.userJwt)
})
.then(app => {
this.app = app
// When the active member (the user) makes a call
// keep a reference to the Call object so we can
// hang up later
this.app.on("member:call", (member, call) => {
this.call = call
});
// Keep track of call status so we know how to
// interact with the call e.g. hangup
this.app.on("call:status:changed", (call) => {
this.callInProgress =
[
"machine",
"timeout",
"unanswered",
"rejected",
"busy",
"failed",
"completed"
].indexOf(call.status) === -1;
})
})
.catch(error => {
console.error(error)
})
},Après la résolution de la promesse de connexion, nous recevons une référence à une représentation de l'Applications via la variable app variable. Nous gardons une référence de cette application pour une utilisation future (this.app) et nous nous lions également à deux événements sur l'application.
Nous lions à member:call qui est déclenché lorsque l'utilisateur actif passe un appel. Dans le gestionnaire d'événement, nous stockons une référence à l'appel en cours avec this.call.
Nous lions également à call:status:changed pour suivre l'état de l'appel statut de l'appel. Dans le gestionnaire d'événement, nous avons mis à jour la propriété callInProgress en fonction de l'état de l'appel. Si l'appel se trouve dans l'un des états finaux, il n'est pas en cours. Dans le cas contraire, l'appel est en cours. Ces états seront reflétés dans la propriété <button> dans le modèle.
Passer un appel téléphonique à partir du navigateur Web
La dernière chose que nous devons faire dans le client - avant d'effectuer une dernière mise à jour sur le serveur et de terminer l'application - est de gérer le clic de l'utilisateur sur le fichier <button>.
...
},
controlCallClick() {
if(this.callInProgress) {
this.call.hangUp()
}
else if(this.phone.isValid) {
this.app.callPhone(this.phone.number)
}
}Ci-dessus, nous avons mis à jour controlCallClick avec la logique permettant de vérifier qu'il n'y a pas d'appel en cours et que l'utilisateur a saisi un numéro de téléphone valide, ce qui devrait déclencher l'appel. S'il y a un appel en cours, il faut raccrocher l'appel. Dans les deux cas, nous appelons la fonction appropriée sur la référence this.call qui a été définie dans le member:call dans le gestionnaire d'événement.
Avec toutes les fonctionnalités côté client en place, vous pouvez saisir un numéro de téléphone valide, cliquer sur le bouton d'appel et vous verrez alors apparaître dans la console une erreur provenant de la plateforme Vonage
conversation:error:not-found
conversation-not-found message in browser console
Lorsqu'un appel est initié ou reçu par la plate-forme Nexmo, celle-ci adresse une requête HTTP à un centre d'appels (ou à un centre d'appels) pour l'application Nexmo correspondante. answer_url pour l'application Nexmo associée correspondante. Le serveur qui reçoit cette requête HTTP doit renvoyer un objet de contrôle de conversation Nexmo (NCCO), un ensemble d'instructions indiquant à Nexmo comment procéder à l'appel.
Connexion du navigateur à un téléphone
Retourner dans server/index.js ajouter un /answer pour traiter la demande GET de la plateforme Vonage :
app.get('/answer', (req, res) => {
const ncco = [{
"action": "connect",
"from": process.env.NEXMO_FROM_NUMBER,
"endpoint": [{
"type": "phone",
"number": req.query.to
}]
}]
res.json(ncco)
})Nexmo attend le retour d'une structure JSON, le NCCO, qui lui indique comment procéder à l'appel. La structure ncco JSON ci-dessus que nous renvoyons informe Nexmo de connect l'appel à un phone avec le numéro identifié par la valeur dans req.query.to - le paramètre to dans la requête GET entrante. Ce numéro est celui que nous avons passé à this.app.callPhone dans notre application Vue.JS.
Notes :
N'oubliez pas que nous n'avons pas d'authentification au niveau de l'application dans notre application web, vous devrez donc l'ajouter vous-même. Par exemple, dans le point de terminaison de l'URL de réponse, vous pouvez vérifier les champs
req.query.toetreq.query.frompour vous assurer que l'utilisateur (identifié parfrom) est autorisé à effectuer l'appel demandé.Si vous disposez d'un numéro de téléphone virtuel Numbers, vous devez ajouter une
NEXMO_FROM_NUMBERdans le fichier.envafin que les destinataires des appels téléphoniques voient un numéro sur leur appel entrant. Sinon, il risque d'apparaître comme "Numéro privé" ou "Inconnu".
Redémarrer le processus Node pour le serveur afin qu'il fonctionne avec le code mis à jour.
Enfin, nous devons permettre à la plateforme Nexmo d'accéder à l'URL de réponse. Pour ce faire, utilisez Ngrok pour créer un tunnel local vers localhost:3000.
$ ngrok http 3000
Ngrok output in a terminalEt mettez à jour les answer_url pour votre Application Nexmo afin d'utiliser les URLs du tunnel Ngrok en utilisant la CLI Nexmo.
$ nexmo app:update NEXMO_APP_ID "call-from-browser" https://4ca73ac6.ngrok.io/answer https://4ca73ac6.ngrok.io/event
Note : vous pouvez trouver le
NEXMO_APP_IDdansserver/.envouserver/.nexmo-app
Retournez dans l'application Vue.JS dans le navigateur, entrez un numéro de téléphone et cliquez sur le bouton pour passer un appel sortant depuis votre navigateur web.
Full Vue.JS Call from Browser application working alongside screenshot of phone ringing
Conclusion
L'objectif de cet article de blog est de montrer comment construire une application qui permet à un utilisateur d'appeler n'importe quel téléphone sur la planète directement à partir d'un navigateur Web en utilisant Vue.JS et In-App Voice en utilisant le Vonage Client SDK pour JavaScript. Il fournit les bases et a, je l'espère, inspiré les cas d'utilisation que cela peut permettre. Oh, et vous pouvez également mettre à jour l'application pour prendre en charge les appels téléphoniques entrants.
Comme indiqué au début de ce billet, In-App Voice est en version d'essai pour les développeurs. N'hésitez pas à nous faire part de vos commentaires sur l'expérience que vous avez eue en construisant cette application ou à nous faire part de toute autre remarque en rejoignant le forum Communauté Vonage Slack.
Où le prochain
Si vous avez trouvé cet article de blog intéressant, les ressources suivantes valent également la peine d'être consultées :