2017-08-13 78 views
1

我使用Sails.js構建了一個使用NodeJS的簡單API端點。NodeJS - 響應流

當有人訪問我的API端點時,服務器開始等待數據,每當有新數據出現時,他都使用套接字廣播它。每個客戶應該根據他的用戶輸入接收他自己的數據流。 (我從來沒有發送過res.json() - 實際發生的事情是瀏覽器不斷加載 - 但上述功能工作)。

2個問題:

  • 我想訂閱和退訂從我的客戶這個API端點(使用RxJS)。當我訂閱時,我開始通過套接字接收數據 - 但我無法取消訂閱API端點(瀏覽器希望完成請求)。

  • 每個客戶端都應該根據請求IP參數訂閱他自己的套接字空間(請參閱更新的代碼)。目前它把這個信息傳達給每個人。

我該如何創建一個類似Sails.js的Stream/Service-like API端點,它會根據他的輸入向每個用戶發送新數據?

我的目標是能夠從每個客戶端訂閱/取消訂閱此API端點。

回答

1

修改稿回答

讓我們假設你的API端點在config/routes.js定義是這樣的:

... 
'get  /collect': 'SomeController.collectSubscribe', 
'delete /collect': 'SomeController.collectUnsubscribe', 

由於每個Cap實例綁定到一個設備,我們需要爲每個訂閱一個實例。我們沒有使用方法join/leave方法,而是跟蹤內存中的Cap實例,並且僅對請求套接字的ID爲broadcast。這是有效的,因爲默認情況下,Sails套接字訂閱了它們自己的ID。

api/controllers/SomeController.js

// In order for the `Cap` instances to persist after `collectSubscribe` finishes, we store them all in an Object, associated with which socket the were created for. 
var caps = {/* req.socket.id: <instance of Cap>, */}; 

module.exports = { 

... 

    collectSubscribe: function(req, res) { 
    if (!res.isSocket) return res.badRequest("I need a websocket! Help!"); 
    if (!!caps[req.socket.id]) return res.badRequest("Dude, you are already subscribed."); 

    caps[req.socket.id] = new Cap(); 
    var c = caps[req.socket.id]; // remember that `c` is a reference to our new `Cap`, not a copy. 
    var device = c.findDevice(req.param('ip')); 

    c.open(device, ...); 
    c.on('data', function(myData) { 
     sails.sockets.broadcast(req.socket.id, 'message', {host: myData}); 
    }); 

    return res.ok(); 
    }, 

    collectUnsubscribe: function(req, res) { 
    if (!res.isSocket) return res.badRequest("I need a websocket! Help!"); 
    if (!caps[req.socket.id]) return res.badRequest("I can't unsubscribe you unless you actually subscribe first."); 

    caps[req.socket.id].removeAllListeners('data'); 
    delete caps[req.socket.id]; 

    return res.ok(); 
    } 
} 

基本上,它是這樣的:當瀏覽器請求觸發collectSubscribe,新Cap實例監聽提供的IP。當瀏覽器觸發collectUnsubscribe時,服務器會檢索該實例,告訴其停止監聽,然後將其刪除。

生產注意事項:請注意,Cap s列表不是數據庫持久存儲器(因爲它存儲在內存中而不是數據庫)!因此,如果您的服務器關閉並重新啓動(由於雷雨天氣等),清單將被清除,但考慮到所有websocket連接都將被丟棄,我不認爲有任何必要擔心這一點。

老回答,不停地爲參考

您可以使用sails.sockets.join(req, room)sails.sockets.leave(req, room)管理插座的房間。基本上你有一個叫做「collect」的房間,只有加入該房間的套接字纔會收到sails.sockets.broadcast(room, eventName, data)

有關如何給用戶的信息sails.socketshere

api/controllers/SomeController.js

collectSubscribe: function(req, res) { 
    if (!res.isSocket) return res.badRequest(); 

    sails.sockets.join(req, 'collect'); 
    return res.ok(); 

}, 

collectUnsubscribe: function(req, res) { 
    if (!res.isSocket) return res.badRequest(); 

    sails.sockets.leave(req, 'collect'); 
    return res.ok(); 
} 

最後,我們需要告訴服務器發送廣播消息到我們的房間'collect'。 請注意,這隻需要發生一次,因此您可以在config/目錄下的文件中執行此操作。

在這個例子中,我把這個config/sockets.js

module.exports = { 
    // ... 
}; 


c.on('data', function(myData) { 
    var eventName = 'message'; 
    var data = {host: myData}; 
    sails.sockets.broadcast('collect', eventName, data); 
}); 

我假設c在這裏訪問;如果沒有,您可以將其定義爲sails.c = ...以使其可全局訪問。

+0

首先,感謝一個真棒的答案!我有一個問題:目前我必須使用從請求'(req.param(「ip」))''得到的參數來初始化'c',並且每個用戶需要在他自己的房間中。 OK('data')'根據用戶輸入發出新數據 – TheUnreal

+0

好的,哇,這是我設想的不同的最終目標!如果你可以用這些具體的細節來編輯你的問題,這將有很大的幫助。另外,我想看看'c'是如何聲明的,因爲它現在看起來與這個問題更相關。謝謝! – ContinuousLoad

+0

對於混淆感到抱歉,只有在我看到您的答案後才提出來。我相應地更新了我的問題 – TheUnreal