質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.93%

mBaaSとPusherを使ったチャットアプリで、チャットの内容がすぐに反映されない

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 405

T_Furuta

score 20

Monaca,ncmb,pusherを用いてmBaaSとPusherを使ってチャットアプリを作るを読み進めながらチャットアプリを作っています。
サンプルコード通りにやってみたのですが、チャットの内容が画面に反映されません。
メッセージを入力して、送信ボタンを押した後、一度ログイン画面に戻ってから再度ログインしなおすと、先程入力したメッセージが表示されます。

index.html

<!DOCTYPE HTML>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
  <meta http-equiv="Content-Security-Policy" content="default-src * data: gap: https://ssl.gstatic.com; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">
  <script src="components/loader.js"></script>
  <script src="lib/onsenui/js/onsenui.min.js"></script>

  <link rel="stylesheet" href="components/loader.css">
  <link rel="stylesheet" href="lib/onsenui/css/onsenui.css">
  <link rel="stylesheet" href="lib/onsenui/css/onsen-css-components.css">
  <link rel="stylesheet" href="css/style.css">
  <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  <script src="https://js.pusher.com/5.1/pusher.min.js"></script>
  <script src="js/app.js"></script>
</head>
<body>
  <ons-navigator swipeable id="nav" page="login.html"></ons-navigator>
  <template id="login.html">
    <ons-page id="login">
      <div class="join-area" style="text-align: center; margin-top: 30px;">
        <h4>チャットルームにログインしてください</h4>
        <p>
          <ons-input id="userName" modifier="underbar" placeholder="ユーザ名" float></ons-input><br />
          <ons-input id="chatId" modifier="underbar" placeholder="chatId" float></ons-input>
        </p>
        <p style="margin-top: 30px;">
          <ons-button onclick="join()">Join</ons-button>
        </p>
      </div>
    </ons-page>
  </template>
</body>
</html>


chat.html

<ons-page id="chat">
    <ons-toolbar>
      <div class="left"><ons-back-button>Back</ons-back-button></div>
      <div class="center"></div>
    </ons-toolbar>
    <input type="hidden" id="channelId" />
    <div class="chat-area">
      <ons-list id="chats"></ons-list>
    </div>
    <div class="send-area">
      <ons-input id="message" placeholder="メッセージ"></ons-input>
      <ons-button onclick="send()" modifier="quiet">送信</ons-button>
    </div>
</ons-page>


app.js

// This is a JavaScript file
let pusher;
let ncmb;
const app_id = 'MY_PUSHER_APP_ID';
const app_cluster = 'MY_PUSHER_CLUSTER';
const applicationKey = 'MY_NCMB_APPLICATION_KEY';
const clientKey = 'MY_NCMB_CLIENT_KEY';

// グローバル変数の処理
ons.ready(function() {
  pusher = new Pusher(app_id, {
      cluster:  app_cluster,
      forceTLS: true
  });
  ncmb = new NCMB(applicationKey, clientKey);
});

// 初期表示処理
$(document).on('init', (event) => {
  if (event.target.id === 'login') {
    $('#chatId').val('channel');
    const userName = localStorage.getItem('userName');
    if (userName)
      $('#userName').val(userName);
  }
});

// チャット画面を表示したときの処理
$(document).on('show', (event) => {
  if (event.target.id === 'chat') {
    const data = event.target.data;
    $('#channelId').val(data.chatId);
    connectPusher(data.userName, data.chatId);
    loadChat(data.userName, data.chatId);
  }
});

// チャット画面から戻った時の処理
$(document).on('hide', (event) => {
  if (event.target.id === 'chat') {
    pusher.unsubscribe($('#channelId').val());
  }
});

// チャットルームにログインする処理
const join = () => {
  const userName = $('#userName').val();
  if (userName === '') {
    ons.notification
      .alert('ユーザ名を決めてください');
    return;
  }
  localStorage.setItem('userName', userName);
  const chatId = $('#chatId').val();
  $('#nav')[0].pushPage('chat.html', {
    data: { userName, chatId }
  })
}

// Pusherに接続する処理
const connectPusher = (userName, chatId) => {
  const channel = pusher.subscribe(chatId);
  channel.bind('message', function(data) {
    showChat(userName, data);
  });
};

// 既存のチャットメッセージを読み込む処理
const loadChat = (userName, channel) => {
  const Chat = ncmb.DataStore('Chat');
  Chat
    .equalTo('channel', channel)
    .fetchAll()
    .then(chats => {
      for (let chat of chats) {
        showChat(userName, chat);
      }
    });
}

// メッセージを一つ表示する処理
const showChat = (userName, data) => {
  if (data.userName == userName) {
    $('#chats').append(`
      <ons-list-item modifier="nodivider">
        <div class="right">
          <ons-button style="background-color: green">${data.message}</ons-button>
        </div>
      </ons-list-item>`);
  }else{
    $('#chats').append(`
      <ons-list-item modifier="nodivider">
        <ons-button>${data.message}</ons-button>
        <span class="list-item__subtitle">${data.userName}</span>
      </ons-list-item>`);
  }
}

// メッセージ送信処理
const send = () => {
  const message = $('#message').val();
  const userName = localStorage.getItem('userName');
  const channel  = $('#channelId').val();
  ncmb.Script
      .data({
        userName,
        message,
        channel
      })
      .exec("POST", "push.js")
      .then(function(res){
        $('#message').val('');
      })
      .catch(function(err){
        console.log(err.name + ':' + err.message);
      });
};


push.js(NCMBスクリプト)
ロジック名   push.js
メソッド    POST
ファイルの状態 実行可能
バージョン   latest

const crypto  = require("crypto");
const request = require('superagent');
const NCMB    = require('ncmb');
module.exports = (req, res) => {  
    const applicationKey = 'MY_NCMB_APPLICATION_KEY';
    const clientKey = 'MY_NCMB_CLIENT_KEY';
    saveDataStore(
        applicationKey,
        clientKey,
        req.body
    ).then(() => {
        return sendPusher(
            'MY_PUSHER_KEY',
            'MY_PUSHER_SECRET',
            'MY_PUSHER_APP_ID',
            req.body
        )
    })
    .then((response) => {
        res.json({});
    })
    .catch((err) => res.status(401).json(err));
};

const saveDataStore = (applicationKey, clientKey, body) => {
    const ncmb = new NCMB(applicationKey, clientKey);
    const Chat = ncmb.DataStore('Chat');
    const chat = new Chat;
    return chat
        .set('channel', body.channel)
        .set('userName', body.userName)
        .set('message', body.message)
        .save()
}

const sendPusher = (authKey, secretKey, appId, body) => {
    const authTimestamp = Date.now() / 1000;
    const authVersion = '1.0';
    const medhod = 'POST';
    const path = `/apps/${appId}/events`;
    const message = {
        data: JSON.stringify({
            userName: body.userName,
            message: body.message
        }),
        name: "message",
        channel: body.channel
    };
    const bodyMd5 = crypto
        .createHash('md5')
        .update(JSON.stringify(message))
        .digest('hex');
    const queryString = `auth_key=${authKey}&auth_timestamp=${authTimestamp}&auth_version=${authVersion}&body_md5=${bodyMd5}`;
    const authSigning = [
        medhod,
        path,
        queryString
    ].join("\n");
    const signature = crypto
        .createHmac("SHA256", secretKey)
        .update(authSigning)
        .digest("hex");
    return request
        .post(`https://api.pusherapp.com${path}?${queryString}&auth_signature=${signature}`)
        .set('Content-Type', 'application/json')
        .send(JSON.stringify(message))
}


なお、push.jsの取得結果ログは下記になります。

{
  "Status": "DONE",
  "RequestHeaders": "{\"host\":\"script.mbaas.api.nifcloud.com\",\"connection\":\"close\",\"content-length\":\"68\",\"x-ncmb-timestamp\":\"2020-03-16T07:13:06.154Z\",\"x-ncmb-signature\":\"GmpGgZ5oupwMzVwp9E8gjM7AcDcibIEJeM9vt6dJR+8=\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 7.0; HUAWEI VNS-L22 Build/HUAWEIVNS-L22; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.132 Mobile Safari/537.36\",\"content-type\":\"application/json\",\"accept\":\"text/plain\",\"x-ncmb-sdk-version\":\"javascript-3.0.2\",\"sec-fetch-dest\":\"empty\",\"x-ncmb-application-key\":\"a49971d96d28c90a9310b798083946a4a87a2b50acff4630558fcba804d0e617\",\"origin\":\"file://\",\"x-requested-with\":\"mobi.monaca.debugger\",\"sec-fetch-site\":\"cross-site\",\"sec-fetch-mode\":\"cors\",\"accept-encoding\":\"gzip, deflate\",\"accept-language\":\"ja-JP,ja;q=0.9,en-US;q=0.8,en;q=0.7\"}",
  "RequestQuery": "{}",
  "ResponseHeaders": "{\"Content-Type\":\"application/json\"}",
  "Error": null,
  "Duration": 1652,
  "created_at": "2020-03-16T07:13:05.000Z",
  "updated_at": "2020-03-16T07:13:07.000Z",
  "ResponseStatus": 401,
  "ScriptIdentifier": "push.js"
}

NCMBのデータストアを確認すると、メッセージは入っているので、そこまでは問題無いと思うので、後はPusherからWebSocket通知がアプリに反映されてないだけだと思うのですが、どこか間違えている部分があればご指摘いただけますでしょうか。
ご教授いただけると助かります。
よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+1

まったく同じ事象ではまっていましたが、NCMBスクリプトの最後のほうにあるpostのURLを以下に変更することで、ResponseStatusが200で返ってくるようになり、pusherからメッセージが届くようになりました。

https://api.pusherapp.com

https://api-ap3.pusher.com

※ URLはPusher管理コンソールの「Getting Started」⇒「2 Add this to your server」⇒「Curl」の2行目から取ってきました。

ご参考になれば幸いです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/03/18 19:30

    ngtosm様

    情報、ありがとうございます。
    今日は外出しているので確認出来ませんが、明日確認させていただきます。

    キャンセル

  • 2020/03/19 09:19

    情報ありがとうございました。
    試してみたのですが、今までメッセージを送信した際、メッセージの内容が消えなかったのですが、消えるようになりました。
    もう少しだと思うので、調査してみます。
    ありがとうございます。

    キャンセル

+1

pusherが正しく初期化できているかどうか確認してください。この部分です。

pusher = new Pusher(app_id, {
      cluster:  app_cluster,
      forceTLS: true
  });

次に、pusherの受信処理が正しくできているかどうかを確認してください。この部分です。

// Pusherに接続する処理
const connectPusher = (userName, chatId) => {
  const channel = pusher.subscribe(chatId);
  channel.bind('message', function(data) {
    showChat(userName, data);
  });
};

pusherの管理画面からメッセージを飛ばしてみてメッセージが受け取れるかどうか確認してみるのもいいかと思います。後もう一息だと思うので、頑張ってください!

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/03/16 17:22

    moongift様

    情報ありがとうございます。ご指摘された点をもう一度確認してみます。

    キャンセル

  • 2020/03/19 09:38

    moongift様
    情報、ありがとうございました。
    確認してみたところ、Pusherの初期化の引数が正しくありませんでした。
    app_idを入れるのでは無く、下記のようにkeyを入れるのが正解でした。
    ```JavaScript
    pusher = new Pusher(pusher_key, {
    cluster: pusher_cluster
    });
    ```

    キャンセル

0

moongift様
ngtosm様

情報ありがとうございました。
お二人のおかげで、無事動作させる事が出来ました。
参考までに、動作したソースコードを下記に記載しておきます。

index.html

<!DOCTYPE HTML>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
  <meta http-equiv="Content-Security-Policy" content="default-src * data: gap: https://ssl.gstatic.com; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">
  <script src="components/loader.js"></script>
  <script src="lib/onsenui/js/onsenui.min.js"></script>

  <link rel="stylesheet" href="components/loader.css">
  <link rel="stylesheet" href="lib/onsenui/css/onsenui.css">
  <link rel="stylesheet" href="lib/onsenui/css/onsen-css-components.css">
  <link rel="stylesheet" href="css/style.css">
  <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  <script src="https://js.pusher.com/5.1/pusher.min.js"></script>
  <script src="js/app.js"></script>
</head>
<body>
  <ons-navigator swipeable id="nav" page="login.html"></ons-navigator>
  <template id="login.html">
    <ons-page id="login">
      <div class="join-area" style="text-align: center; margin-top: 30px;">
        <h4>チャットルームにログインしてください</h4>
        <p>
          <ons-input id="userName" modifier="underbar" placeholder="ユーザ名" float></ons-input><br />
          <ons-input id="chatId" modifier="underbar" placeholder="chatId" float></ons-input>
        </p>
        <p style="margin-top: 30px;">
          <ons-button onclick="join()">Join</ons-button>
        </p>
      </div>
    </ons-page>
  </template>
</body>
</html>

chat.html

<ons-page id="chat">
    <ons-toolbar>
      <div class="left"><ons-back-button>Back</ons-back-button></div>
      <div class="center"></div>
    </ons-toolbar>
    <input type="hidden" id="channelId" />
    <div class="chat-area">
      <ons-list id="chats"></ons-list>
    </div>
    <div class="send-area">
      <ons-input id="message" placeholder="メッセージ"></ons-input>
      <ons-button onclick="send()" modifier="quiet">送信</ons-button>
    </div>
  </ons-page>
// This is a JavaScript file
let pusher;
let ncmb;

const pusher_key = 'MY_PUSHER_KEY';    //ここを、app_idから、Keyに変更しました。
const pusher_cluster = 'MY_PUSHER_CLUSTER';
const applicationKey = 'MY_NCMB_APPLICATION_KEY';
const clientKey = 'MY_NCMB_CLIENT_KEY';

// グローバル変数の処理
ons.ready(function() {
  pusher = new Pusher(pusher_key, {
      cluster:  pusher_cluster
  });
  ncmb = new NCMB(applicationKey, clientKey);
});

// 初期表示処理
$(document).on('init', (event) => {
  if (event.target.id === 'login') {
    $('#chatId').val('channel');
    const userName = localStorage.getItem('userName');
    if (userName)
      $('#userName').val(userName);
  }
});

// チャット画面を表示したときの処理
$(document).on('show', (event) => {
  if (event.target.id === 'chat') {
    const data = event.target.data;
    $('#channelId').val(data.chatId);
    connectPusher(data.userName, data.chatId);
    loadChat(data.userName, data.chatId);
  }
});

// チャット画面から戻った時の処理
$(document).on('hide', (event) => {
  if (event.target.id === 'chat') {
    pusher.unsubscribe($('#channelId').val());
  }
});

// チャットルームにログインする処理
const join = () => {
  const userName = $('#userName').val();
  if (userName === '') {
    ons.notification
      .alert('ユーザ名を決めてください');
    return;
  }
  localStorage.setItem('userName', userName);
  const chatId = $('#chatId').val();
  $('#nav')[0].pushPage('chat.html', {
    data: { userName, chatId }
  })
}

// Pusherに接続する処理
const connectPusher = (userName, chatId) => {
  console.log(userName);
  console.log(chatId);

  const channel = pusher.subscribe(chatId);
  channel.bind('message', function(data) {
    showChat(userName, data);
  });
};

// 既存のチャットメッセージを読み込む処理
const loadChat = (userName, channel) => {
  const Chat = ncmb.DataStore('Chat');
  Chat
    .equalTo('channel', channel)
    .fetchAll()
    .then(chats => {
      for (let chat of chats) {
        showChat(userName, chat);
      }
    });
}

// メッセージを一つ表示する処理
const showChat = (userName, data) => {
  if (data.userName == userName) {
    $('#chats').append(`
      <ons-list-item modifier="nodivider">
        <div class="right">
          <ons-button style="background-color: green">${data.message}</ons-button>
        </div>
      </ons-list-item>`);
  }else{
    $('#chats').append(`
      <ons-list-item modifier="nodivider">
        <ons-button>${data.message}</ons-button>
        <span class="list-item__subtitle">${data.userName}</span>
      </ons-list-item>`);
  }
}

// メッセージ送信処理
const send = () => {
  const message = $('#message').val();
  const userName = localStorage.getItem('userName');
  const channel  = $('#channelId').val();

  ncmb.Script
      .data({
        userName,
        message,
        channel
      })
      .exec("POST", "push.js")
      .then(function(res){
        $('#message').val('');
      })
      .catch(function(err){
        console.log(err.name + ':' + err.message);
      });
};

push.js(NCMBスクリプト)
ロジック名   push.js
メソッド    POST
ファイルの状態 実行可能
バージョン   latest

const crypto  = require("crypto");
const request = require('superagent');
const NCMB    = require('ncmb');
module.exports = (req, res) => {  
    const applicationKey = 'MY_NCMB_APPLICATION_KEY';
    const clientKey = 'MY_NCMB_CLIENT_KEY';
    const pusher_key = 'MY_PUSHER_KEY';
    const pusher_secret = 'MY_PUSHER_SECRET';
    const pusher_appid = 'MY_PUSHER_APP_ID';
    saveDataStore(
        applicationKey,
        clientKey,
        req.body
    ).then(() => {
        return sendPusher(
            pusher_key  ,
            pusher_secret  ,
            pusher_appid  ,
            req.body
        )
    })
    .then((response) => {
        res.json({});
    })
    .catch((err) => res.status(401).json(err));
};

const saveDataStore = (applicationKey, clientKey, body) => {
    const ncmb = new NCMB(applicationKey, clientKey);
    const Chat = ncmb.DataStore('Chat');
    const chat = new Chat;
    return chat
        .set('channel', body.channel)
        .set('userName', body.userName)
        .set('message', body.message)
        .save()
}

const sendPusher = (authKey, secretKey, appId, body) => {
    const authTimestamp = Date.now() / 1000;
    const authVersion = '1.0';
    const medhod = 'POST';
    const path = `/apps/${appId}/events`;
    const message = {
        data: JSON.stringify({
            userName: body.userName,
            message: body.message
        }),
        name: "message",
        channel: body.channel
    };
    const bodyMd5 = crypto
        .createHash('md5')
        .update(JSON.stringify(message))
        .digest('hex');
    const queryString = `auth_key=${authKey}&auth_timestamp=${authTimestamp}&auth_version=${authVersion}&body_md5=${bodyMd5}`;
    const authSigning = [
        medhod,
        path,
        queryString
    ].join("\n");
    const signature = crypto
        .createHmac("SHA256", secretKey)
        .update(authSigning)
        .digest("hex");
    return request
     //ここを、https://api.pusherapp.comから、https://api-ap3.pusher.comに変更しました。
        .post(`https://api-ap3.pusher.com${path}?   
 ${queryString}&auth_signature=${signature}`)
        .set('Content-Type', 'application/json')
        .send(JSON.stringify(message))
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/04/24 06:24

    僕もちょうどこの情報について興味があったので大変参考になりました。ありがとうございます。
    T_Furutaさん最後のソースコードのpush.jsにPusherのキー諸々が表示されているので、隠したほうがよろしいかいと思います。

    キャンセル

  • 2020/04/24 09:59

    todayyy様
    多少参考になったようで良かったです。
    また、ソースコードのご指摘ありがとうございました。
    見逃していたため、助かりました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.93%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る