
ブラウザでSMS:ウェブソケットの冒険
所要時間:7 分
Nexmo Messages APIによって行われる重要な作業の多くはサーバーサイドで行われます。メッセージの送信、受信、処理などが行われますが、通常、APIを利用する多くのアプリケーションのエンドユーザーからは、このアクティビティはすべて隠されています。
ほとんどの場合、エンドユーザーへのこれらのメッセージの表示は、それぞれのアプリケーションによって処理される。ブラウザでこれらのメッセージを表示するオプションもあるが、そのためにはサーバーからブラウザにメッセージをプッシュする何らかの方法が必要になる。そのためには、サーバーからブラウザにメッセージをプッシュする方法が必要になる。
これはかなり長いチュートリアルで、HTML、CSS、Javascript、および Node.js.すでに何が何だかわかっている場合は、どのセクションも自由に読み飛ばしてください。
SMSを送受信できる "仮想 "電話をブラウザ上で構築する手順を説明します。使用するのは Glitchを使ってアプリケーションをホストするので、セットアップ手順の一部はGlitch向けです。
Glitchは、開発者がサーバーのセットアップに煩わされることなく、アプリのビルドとデプロイをすぐに実行できるオンライン開発者環境です。このプラットフォーム上のアプリはすべてリミックスやパーソナライズが可能で、コードを共有し、仕組みを理解するのに最適な場所となっている。
また、Node.jsがインストールされている環境であれば、どのような環境でも使うことができる。このプロジェクトの依存関係はすべて npm.
初期設定と構成
GlitchでKoa.jsアプリを始める
Glitchは常にインターフェースと機能を改善しています。 サインインボタンをクリックし、GitHubまたはFacebook経由でログインするか、メールアドレスでサインアップしてください。
Glitch sign in
その後 新規プロジェクトボタンをクリックする。3つの選択肢があります、 ホームページ, ハローエクスプレスそして hello-sqlite.このチュートリアルでは hello-expressを使用することで、Node.jsとnpmがすでにインストールされた環境が得られるからだ。
Glitch hello-express
追加のノードパッケージをインストールするには、コマンドラインから コンソールボタンをクリックします。
Console
ログウィンドウを切り替えるには ログボタンをクリックして切り替えることができる。そこから、bash環境で標準的なCLIコマンドをすべて使うことができる。唯一の違いは、Glitchでは pnpmの代わりに npm.
グリッチの使用 エクスプレスをデフォルトのNode.jsフレームワークとして使用していますが、アプリを Koa.jsへの変換はそれほど複雑ではありません。Koaは、ES2015と非同期関数をサポートするために、node v7.6.0以上が必要であることに注意してください。
Dependencies
削除 expressそして body-parserをプロジェクトから削除する:
以下のコマンドでKoa.jsとkoa-bodyparserをインストールする:
コンソールとエディターは自動的に同期しないので、次のコマンドを実行してください。 refreshコマンドを実行して package.jsonコマンドを実行してください。
Koa
また、アプリケーションのステータスがエラーを表示していることにも気づくだろう。デフォルトの server.jsファイルがまだ express.
これを修正するには server.jsの内容を次のコードに置き換える:
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello Dinosaur';
});
app.listen(3000);
アプリケーションを表示しようとすると、空白のページが表示されます。 こんにちは、恐竜.
Koa.jsで静的ファイルを扱う
私たちは、ユーザーが受信者の電話番号やメッセージのようなSMSを送信するための関連情報を入力できるように、入力フィールドを備えた基本的なHTMLページを提供する必要があります。
koa-staticはKoa用の静的ファイルサービングミドルウェアです。以下のコマンドでコンソールからプロジェクトにインストールしてください:
物事を複雑にしないために、ランディングページに必要なすべてのファイルを publicフォルダに配置します。ファイルを index.htmlファイルを viewsフォルダから publicフォルダに移動させることができます。
Rename file
コンソールからコマンドラインで行うこともできる。
それが終わったら server.jsファイルを修正する。 publicフォルダから以下のようにファイルを配信する:
const serve = require('koa-static');
const Koa = require('koa');
const app = new Koa();
app.use(serve('./public'));
app.listen(3000);
console.log('listening on port 3000');の代わりに hello worldの代わりに、アプリはデフォルトのGlitch index.htmlファイルを提供しなければならない。このファイルは、チュートリアルの後半で電話のインターフェイスを模倣するように変更します。
Nexmo APIを使い始める
Dashboard
Glitchアプリに戻って Node.js用Nexmo REST APIクライアントをインストールする:
Messages APIは現在まだベータ版なので、ベータ版を使用しています。プロジェクトを更新すると package.jsonはこのようになっているはずです:
Nexmo node
これで基本的なセットアップは完了です。これはNexmoのAPIを利用したKoaアプリケーションの出発点となるもので、このプロジェクトをGlitchのスターターキットにして、そこからリミックスすることができます。
ウェブソケットとは?
Webソケットプロトコル WebSocketプロトコルは、クライアントとリモートホスト間の双方向通信を可能にするために、インターネット・エンジニアリング・タスク・フォース(IETF)によって開発された。
通信プロトコルの一種で HTTP(ハイパーテキスト転送プロトコル), FTP(ファイル転送プロトコル)または SMTP(簡易メール転送プロトコル).通信プロトコルは、マシン同士の通信を可能にする。ほとんどの通信プロトコルは、インターネットを介して2つのマシン間のオープンな接続を維持します。
ウェブが動作するHTTPはそれとは異なる。HTTPはリクエスト/レスポンス・モードで動作するため、コネクションレス・プロトコルとして知られている。ウェブ・ブラウザは画像、フォント、コンテンツなどをサーバーに要求しますが、要求が満たされると、ブラウザとサーバー間の接続は切断されます。
HTTP upgradeヘッダーを使用して、既存のHTTP接続を互換性のない新しいプロトコルにアップグレードすることが可能です。接続リクエストは 常に接続リクエストは常にクライアントによって開始されます。 426 Upgrade RequiredHTTP レスポンスをクライアントに返すことでアップグレードを強制することができます。
HTTP/1.1 接続は、TLS 接続(推奨される方法ではありません)、HTTP/2 接続、またはアップグレードの最も一般的な使用例である WebSocket 接続にアップグレードすることができます。クライアントとサーバーの間で WebSocket 接続を確立するには、アップグレード・ヘッダーを含む最初の HTTP/1.1 リクエストが必要です。
最初のヘッダーを簡略化すると次のようになる:
GET /chat HTTP/1.1
Host: server.example.com
Origin: http://example.com
Upgrade: websocket
Connection: UpgradeサーバーがWebSocketプロトコルをサポートしていれば、アップグレード要求に同意し、ハンドシェイクに対して 101ステータスコードを返します:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade以外の場合は 101以外の場合は、WebSocket ハンドシェイクが完了していないことを示します。ハンドシェイクが完了すると、最初の HTTP/1.1 接続は WebSocket 接続に切り替わり、2 台のマシン間でデータを送受信できるようになります。
ソケットAPI WebSockets APIは、ウェブアプリケーションで 使用するWebSocketプロトコルを使用することができるインターフェースである。2012年以降にリリースされたほとんどのブラウザがWebSocket APIをサポートしているため、サポートはそれなりに充実している。
WebSocket オブジェクトは、サーバーへの WebSocket 接続の作成と管理、および接続上のデータ送受信のための API を提供します。新しい WebSocket オブジェクトは WebSocket コンストラクタで作成できます、 WebSocket(url[, protocols]).WebSocket コンストラクタに渡す URL は wsまたは wssスキームでなければなりません。
const socket = new WebSocket('ws://localhost:8080')WebSocketオブジェクトは4つのイベントをディスパッチする:
openerrormessageclose
WebSocket接続が確立されると openイベントが発生します。先ほどのコードのブロックで作成した WebSocket オブジェクトを参照すると、対応するイベントハンドラは次のようになります:
socket.onopen = function(evt) {
// do something
}デバッグのために errorイベントを使って、デバッグのためにエラーをコンソールに記録することができる:
socket.onerror = function(evt) {
console.log('WebSocket error: ', evt)
// include an error event handler here
}最も一般的なのは messageイベントを利用する。例えば、サーバーからテキストメッセージが送信された場合、次のようにコンソールにログを記録することができる:
socket.onmessage = function(evt) {
console.log('Message: ', evt.data)
}最後に closeイベントが発生します。このイベントが発生すると、新しい接続が確立されるまで、クライアントとサーバーは互いにメッセージを送信できなくなります。
socket.onclose = function(evt) {
console.log('WebSocket connection closed. ', evt)
}また、WebSocketオブジェクトには2つのメソッドがあります、 send()サーバーにデータを送信するメソッドと close()既存の WebSocket 接続を終了するためのメソッドがあります。
この send()メソッドは、接続が開いている間だけ動作する。これは当然のことのように思えるが、コードの構造を間違えると、接続が開く前、あるいは接続が閉じた後にトリガーされてしまう可能性がある。
// Listen for the open event before triggering the sendMsg() function
socket.onopen = function(evt) {
console.log('Connection established')
sendMsg('Hello Nexmo!')
}
// Send a message through the WebSocket connection
function sendMsg(data) {
socket.send(data)
}WebSocketとは何か、WebSocket APIがどのように動作するかについて理解を深めたところで、この知識を活用して、ブラウザ経由で直接SMSを送受信する基本的なアプリケーションを作成する準備が整いました。 NexmoメッセージAPI.
NexmoでブラウザからSMSを受信する
バーチャル電話番号の取得
Messages APIを使ってSMSを送受信するには、次のものが必要です。 仮想電話番号通常の電話番号と同じですが、物理的な電話回線やデバイスに縛られることはありません。
Numbersからバーチャル番号を購入することができます。 Numbersセクションで Numbersを購入する.ご希望の国の現地番号、サポートする機能、携帯電話、固定電話、フリーダイヤルなどの番号の種類を選択できます。
Buy numbers
マイナンバーが発行されると、以下のページに表示されます。 あなたのNumbersセクションに表示されます。右端にある鉛筆のアイコンをクリックしてください。 管理列の下にある鉛筆のアイコンをクリックして、受信ウェブフックURLを設定します。これはSMSを受信するために必要です。SMSがあなたの番号に送信されると、このURLにメッセージのペイロードと共にリクエストが送信されます。 POSTリクエストがメッセージのペイロードと共にこのURLに送信されます。
Inbound webhook URL
Glitchを使用している場合、ウェブフックのURLは次のようになります。 https://boom-meal.glitch.me/inbound-sms.ルートとして inbound-smsをルートとして使用することをお勧めしますが、そのルートがあなたのアプリケーションで有効である限り、お好きなものを使用できます。WebhookのURLは、アプリケーションがどこでホストされているかによって適宜調整してください。
もうひとつ確認すべきことは、あなたのアカウントのデフォルトのSMS設定が、HTTPメソッドで POSTに設定されていることです。
Settings
インバウンドSMSの受信
着信SMSを受信するには、アプリケーションにルートを追加して、誰かがあなたの仮想番号にSMSを送信したときに発生する着信リクエストを処理する必要があります。 POSTリクエストを処理するルートをアプリケーションに追加する必要があります。ExpressやHapi.jsのような他の一般的なNode.jsフレームワークとは異なり、ルーティングは別のモジュールによって処理されるので、最初にそれをインストールする必要があります。
あなたの server.jsファイルに追加する:
const bodyParser = require('koa-bodyparser');
const Router = require('koa-router');
const router = new Router();
app.use(bodyParser());
// Make sure this is the route you configured for your virtual number
router.post('/inbound-sms', async (ctx, next) => {
const payload = ctx.request.body;
console.log(payload);
ctx.status = 200;
});
app.use(router.routes()).use(router.allowedMethods());
このチュートリアルの前の方のセットアップ手順に従ったのであれば koa-bodyparserがインストールされているはずです。リクエストボディを処理するには、ボディパーサーが必要です。すべてが正しく接続されていることを確認するには、あなたのバーチャル番号にSMSを送信する。メッセージのペイロードがコンソールに記録されるはずです。
SMS payload
WebSocketサーバーの作成
これで動作が確認できたので、コンソールにメッセージのペイロードを記録する代わりに、WebSocketを使って関連情報をブラウザに送信してみよう。
その前に、Node.jsのWebSocketヘルパー・ライブラリーである ws.
これが終わったら wsを server.jsファイルを以下のように使用することで、ウェブ・ソケット・サーバーを作成することができる:
const http = require('http');
const WebSocket = require('ws');
// Create a web socket server on top of a regular http server
const server = http.createServer(app.callback());
const wsserver = new WebSocket.Server({ server: server });オリジナルを app.listen(3000)と server.listen(3000).
さらに、受信メッセージのペイロードを処理する関数を追加し、このデータをブラウザに送信してみよう。
function receiveSms(payload) {
const phone = payload.msisdn;
const msg = payload.text;
const timestamp = payload['message-timestamp'];
const smsData = JSON.stringify({
phone: phone,
msg: msg,
timestamp: timestamp
});
// Broadcast the message to all open clients
wsserver.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(smsData);
}
});
}
チュートリアルのこの時点までの server.jsチュートリアルのここまでのファイル全体は次のようになるはずだ:
const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const Router = require('koa-router');
const serve = require('koa-static');
const http = require('http');
const WebSocket = require('ws');
const app = new Koa();
const router = new Router();
const server = http.createServer(app.callback());
const wsserver = new WebSocket.Server({ server: server });
app.use(serve('./public'));
app.use(bodyParser());
router.post('/inbound-sms', async (ctx, next) => {
const payload = ctx.request.body;
receiveSms(payload);
ctx.status = 200;
});
app.use(router.routes()).use(router.allowedMethods());
server.listen(3000);
console.log('listening on port 3000');
function receiveSms(payload) {
const phone = payload.msisdn;
const msg = payload.text;
const timestamp = payload['message-timestamp'];
const smsData = JSON.stringify({
phone: phone,
msg: msg,
timestamp: timestamp
});
// Broadcast the message to all open clients
wsserver.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(smsData);
}
});
}
ブラウザで受信SMSを表示する
次のステップは、ブラウザがサーバーからこのデータを受け取る準備ができていることを確認することだ。
グリッチが提供する client.jsファイルのデフォルトの内容を消去し、その内容を以下のように置き換える:
document.addEventListener("DOMContentLoaded", function() {
let socket = null;
let connected = false;
function start() {
socket = new WebSocket('wss://' + location.host);
socket.onopen = function(evt) {
connected = true;
console.log('open')
}
socket.onmessage = function(evt) {
const reply = JSON.parse(evt.data);
console.log(reply);
addMsg(reply.phone, reply.msg, reply.timestamp, 'messages');
}
socket.onclose = function(evt) {
connected = false;
console.log('closed')
check();
}
}
function check() {
if(!socket || socket.readyState == 3) start();
}
start();
setInterval(check, 5000);
});ここには2つの関数がある、 start()新しい WebSocket 接続と対応するイベントハンドラを確立する関数と check()接続の状態をチェックし、切断された場合は再接続します。
さて、アプリケーションのブラウザ・コンソールを開いてしばらく放っておくと、一連の openと closeがコンソールに記録されているはずです。
Check
要素のデフォルトのマークアップを index.htmlファイルのデフォルトのマークアップを次のように置き換える:
<main>
<h1>V-mobile</h1>
<ul id="messages"></ul>
</main>もっと携帯電話らしく見せるための要素やスタイルは、後で追加できる。とりあえず、まずはすべてのデータを正しい場所に表示できるようにしよう。さらに2つの関数を client.jsファイルに、サーバーから送られてきたデータを解析してDOMに追加する関数を2つ追加します。
function addMsg(sender, content, time, parent) {
const messages = document.getElementById(parent);
const newMsg = document.createElement('li');
messages.appendChild(newMsg);
appendData(newMsg, sender);
appendData(newMsg, content);
appendData(newMsg, time);
}
function appendData(newMsg, data) {
const textSpan = document.createElement('span');
const textContent = document.createTextNode(data);
textSpan.appendChild(textContent);
newMsg.appendChild(textSpan);
}この新しい関数を onmessageイベント・ハンドラにこの新しい関数を追加すると、バーチャル番号にSMSを送信したときに、そのSMSがページに表示されるようになります。
socket.onmessage = function(evt) {
const reply = JSON.parse(evt.data);
console.log(reply);
addMsg(reply.phone, reply.msg, reply.timestamp, 'messages');
}
First Inbound
NexmoのMessages APIを使ってブラウザからSMSを送信する
メッセージアプリケーションの作成
次に、Nexmoのダッシュボードに戻り、「アプリケーションの作成」に移動します。 アプリケーションの作成ページに移動します。 メッセージとディスパッチセクションに移動します。アプリケーション名を入力し、GlitchアプリのURLをホストとしてウェブフックのURLを入力します。また、公開鍵と秘密鍵のペアを生成する必要があります。 private.keyファイルをダウンロードするよう促されます。
Generate key
ウェブフックURLの両方が設定されていること、そしてアプリケーションがこれらのエンドポイントの両方に対してリクエストを受け付けるルートを設定していることが重要です。 POSTリクエストを受け入れるためのルートが設定されていることが重要です。
次に、オレンジ色の アプリケーションの作成ボタンをクリックします。次の画面では、バーチャル・ナンバーと申請書をリンクさせることができます。 リンクボタンをクリックします。 管理ボタンをクリックします。
Create a message application
最後に、外部アカウントをリンクするかどうか尋ねられますが、今はそのままにしておいてください。
External accounts
ファイルを private.keyファイルをGlitchにアップロードし、それを秘密にするには、ファイルを .dataフォルダにファイルを作成することができます。このフォルダの内容は、あなたと、あなたがプロジェクトに追加した信頼できる共同作業者のみが見ることができます。先ほどダウンロードした private.keyの内容をこの新しいファイルにコピーしてください。
Private key
認証情報の設定
グリッチは 環境変数をサポートしている。 .envファイルを通して環境変数をサポートしています。これは、API認証情報やプロジェクトのその他のプライベートデータを安全に保存する方法です。APIキー、シークレット、Nexmo仮想番号、MessagesアプリケーションID、秘密鍵パスを .envファイルに設定します。
それぞれの値は文字列である必要があるので、必ず引用符で囲んでください。SMSメッセージの送信に使用する新しいNexmoインスタンスを初期化するために、これらの値を参照します。
env file
API認証情報を server.jsファイルに追加し、新しいNexmoインスタンスを初期化します。
const NEXMO_API_KEY = process.env.NEXMO_API_KEY;
const NEXMO_API_SECRET = process.env.NEXMO_API_SECRET;
const NEXMO_APPLICATION_ID = process.env.NEXMO_APPLICATION_ID;
const NEXMO_APPLICATION_PRIVATE_KEY_PATH = process.env.NEXMO_APPLICATION_PRIVATE_KEY_PATH;
const NEXMO_NUMBER = process.env.NEXMO_NUMBER;
const Nexmo = require('nexmo');
const nexmo = new Nexmo({
apiKey: NEXMO_API_KEY,
apiSecret: NEXMO_API_SECRET,
applicationId: NEXMO_APPLICATION_ID,
privateKey: NEXMO_APPLICATION_PRIVATE_KEY_PATH
}); ブラウザからSMSを送信する
ユーザーが受信者の電話番号と送信したいメッセージを入力する方法が必要なので、そのための入力フィールドをいくつか追加しよう。
<input type="tel" name="phone" placeholder="Send SMS to…?" required>
<input type="text" name="msg">
<button id="send" type="button">Send</button>
Inputs
ユーザー入力を処理するには、以下を client.jsファイルに追加する:
const msgField = document.querySelector('input[name="msg"]');
const phoneField = document.querySelector('input[name="phone"]');
const sendBtn = document.getElementById('send');
msgField.addEventListener('keypress', (evt) => {
if (evt.keyCode === 13) {
const passed = phoneCheck(phoneField);
if (passed) {
send(socket, phoneField, msgField);
} else {
console.log('Phone field was not filled. To do: add error handling UI here.');
}
}
}, false);
sendBtn.addEventListener('click', (evt) => {
const passed = phoneCheck(phoneField);
if (passed) {
send(socket, phoneField, msgField);
} else {
console.log('Phone field was not filled. To do: add error handling UI here.');
}
}, false);
function phoneCheck(phoneField) {
return phoneField.value !== '' && /^\d+$/.test(phoneField.value);
}
function send(socket, phoneField, msgField) {
const phone = phoneField.value.replace(/\D/g,'');
const msg = msgField.value;
const payload = JSON.stringify({
phone: phone,
message: msg
});
socket.send(payload);
const date = new Date();
const time = date.toLocaleTimeString();
addMsg('YOUR_APP_NAME', msg, time, 'messages');
msgField.value= '';
}
ここでやっているのは、ユーザーがクリックしたときに send()関数をトリガすることです。 送信ボタンまたは Enterキーをクリックしたときに関数を起動することです。また、電話番号フィールドには非常に初歩的なチェック機能があり、数字のみが入力されていることを確認します。メッセージは、一般的なメッセージング・アプリケーションのように、ブラウザ上にも表示されます。
たくさんある。 セキュリティとUXの観点からセキュリティとUXの観点から、フィールドのバリデーションにはもっとやるべきことがたくさんあり、このチュートリアルの範囲外ですが、バリデーションは重要であり、それが正しく行われることを確認するために労力とテストを費やすことは常にベストプラクティスです。
サーバー側では、ブラウザから送られてきたデータを受信者の電話番号に転送する必要がある。
以下のルートを server.jsファイルに追加する:
router.post('/webhooks/inbound-message', async (ctx, next) => {
ctx.status = 200;
});
router.post('/webhooks/message-status', async (ctx, next) => {
ctx.status = 200;
});
これは POSTこれは、Nexmoからのリクエストが適切なエンドポイントに送信され、正常に配信されたときにレスポンスを受け取れるようにするためである。 200レスポンスを受け取れるようにするためです。
メッセージ・ハンドラと forwardSMS()関数を追加します。この関数はNexmoのMessages APIを利用し、ブラウザからのデータを受信したら、対象の受信者にSMSを送信します。
wsserver.on('connection', function connection(socket) {
socket.on('message', data => {
forwardSms(JSON.parse(data));
});
});
function forwardSms(payload) {
const phone = payload.phone;
const msg = payload.message;
nexmo.channel.send(
{ "type": "sms", "number": phone },
{ "type": "sms", "number": NEXMO_NUMBER },
{
"content": {
"type": "text",
"text": msg
}
},
(err, data) => {
if (err) { console.log(err) }
if (data) { console.log('Outbound message successful: ', data.message_uuid) }
}
)
}
すべてが期待通りに動作することをテストするには、アクセス可能な携帯電話の電話番号を入力し、テストメッセージを送信します。対象の受信者は数秒以内にSMSを受信するはずです。
Forward SMS
対象の受信者がSMSスレッドに返信した場合、メッセージはブラウザに送り返されます。
Reply
基礎となるシステムが稼働し始めた今、このプロジェクトを続けたいのであれば、インターフェイスをより美しくしたり、クライアントとサーバーの両方でより強力なバリデーション・チェックを追加したり、エッジケースを処理したりなど、できることがいくつかある。
このプロジェクトの私のバージョンはV-mobileと呼ばれている。 グリッチ.もし気に入ったら、僕のプロジェクトを自由にリミックスして自分のものにしてほしい。
End result
次はどこだ?
これらのAPIをもっと使いたいとお考えなら、以下のリンクが参考になるでしょう:
ドキュメント開発者ポータルの Messages API に関するドキュメント
シリーズ チュートリアル様々なNexmo API
ご用の際は NexmoコミュニティSlackチャンネル
ご意見・ご感想は下記までツイートしてください。 @NexmoDev