Laravel Broadcasting 同时启用 web 和 api 两个 Middleware

妙啊

Posted by cj on July 16, 2021

Laravel Broadcasting 同时启用 web 和 api 两个 Middleware

最近在给项目做 API 接口,大体搞定了,但是缺少实时推送功能。既然 web 版可以用 websocket 接收推送,api 为何不可呢?

核心在 app/Providers/BroadcastServiceProvider.php:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\Facades\Route;
use Illuminate\Http\Request;

class BroadcastServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        if (request()->hasHeader('Authorization')){
            Broadcast::routes(['middleware' => 'auth:api']);
        }else{
            Broadcast::routes();
        }

        require base_path('routes/channels.php');
    }
}

如果请求头中带有 Authorization,则走 auth:api 中间件,不带就走 auth:web

laravel-echo-server.jsonauthEndpoint 字段仍然保持不变:/broadcasting/auth

新增一个 resources/js/test.js 用来测试 api

window.$ = window.jQuery = require('jquery');

import Echo from "laravel-echo"

window.io = require('socket.io-client');

window.Echo = new Echo({
    broadcaster: 'socket.io',
    host: window.location.hostname,
    auth:
    {
        headers:
        {
            'Authorization': 'Bearer {ACCESS_TOKEN_GOES_HERE}'
        }
    }
});

暂时把 access_toekn 写死了,我目的并不是在网页上这样用,而是要在 APP里 或者 C++ 客户端里使用 websocket 接收推送,所以这里写死了做个验证。

不要忘了修改 webpack.mix.js 然后 yarn dev 编译一下:

mix.js('resources/js/app.js', 'public/js')
   .sass('resources/sass/app.scss', 'public/css')
   .sass('resources/sass/wechat.scss', 'public/css')
   .js('resources/js/test.js', 'public/js')
   .version();

新增一个 web 页面 resources/views/pages/test_websocket.blade.php:

<html>
<body>
<p>     
    Websocket Test
</p>

<script src="\{\{ mix('js/test.js') \}\}"></script>
<script src="https://\{\{Request::getHost()\}\}/socket.io/socket.io.js"></script>
<script>
  
$(document).ready(function(){
  Echo.private('some-private-channel')      
        .listen('.some-event', (e) => {
            console.log(e);     
        });
});
    
</script>
</body>
</html>

打开这个网页,进入控制台,可以接收推送;打开原有的走 auth:web 的带有推送功能的网页,可以接收推送,大功告成。

Android 客户端 调用 api 获取 token 后加入 websocket channel 接收推送

引入 nkzawa/socket.io-android-chat 即可。 加入 private-channel 示例, 在连接成功回调处:

SocketIOPrivateChannel privateChannel = echo.privateChannel("channeName");
privateChannel.listen(".EventName", args1 -> {
    // event callback goes here
});

C++ 客户端调用 api 获取 token 后加入 websocket channel 接收推送

引入 socketio/socket.io-client-cpp 即可。 加入 private-channel 示例:


// 注册 event 回调
current_socket->on("EventName", sio::socket::event_listener_aux([&](string const& name, message::ptr const& data, bool isAck, message::list& ack_resp)
{
    _lock.lock();
    cout << name << endl;
    print_msg(data);
    _lock.unlock();
}));


// 加入 private channel
auto headers = std::dynamic_pointer_cast<sio::object_message>(sio::object_message::create());
headers->insert("Authorization", "Bearer " + access_token);

auto auth = std::dynamic_pointer_cast<sio::object_message>(sio::object_message::create());
auth->insert("headers", headers);

auto json = std::dynamic_pointer_cast<sio::object_message>(sio::object_message::create());
json->insert("channel", "private-channelName");
json->insert("auth", auth);

auto list = sio::message::list(json);
current_socket->emit("subscribe", list);

如果要连接 https 服务器,则要定义 SIO_TLS 后重新编译。

Reference