Quart python: отправить широковещательное сообщение websocket из curl и отобразить его

#python #websocket #messaging #quart

#python #websocket #обмен сообщениями #quart

Вопрос:

я отредактировал этот способ:

моя команда curl:

 curl -d '{"key1":"value1", "key2":"value2"}' -H "Content-Type: application/json" -X POST http://localhost:5000/telepath
  

я хочу отображать эти опубликованные данные на всех клиентах.
я собрал все, что мог, в документации, но мне это нелегко.

вот мой скрипт:

 from quart import Quart, render_template, websocket
from functools import partial, wraps
from quart import request, redirect, url_for, copy_current_websocket_context
import asyncio

app = Quart(__name__)

connected_websockets = set()

def collect_websocket(func):
    @wraps(func)
    async def wrapper(*args, **kwargs):
        global connected_websockets
        queue = asyncio.Queue()
        connected_websockets.add(queue)
        try:
            return await func(queue, *args, **kwargs)
        finally:
            connected_websockets.remove(queue)
    return wrapper

async def broadcast(message):
    for queue in connected_websockets:
        await queue.put(message)

@app.route('/')
async def index():
    return await render_template('index.html')


@app.websocket('/ws')
@collect_websocket
async def ws(queue):
    print("$ $ $",queue)
    while True:
        data = await websocket.receive()
        print("n {}".format(data))
        await websocket.send(f"echo {data}")



@app.route('/telepath', methods=['POST'])
async def telepath():
    global connected_websockets    
    data = await request.get_json()
    for queue in connected_websockets:
        await queue.put(data["key1"])
    return "n Request Processed.n"



if __name__ == '__main__':
    app.run(port=5000)
  

и шаблон:

 <!doctype html>
<html>
  <head>
    <title>My TEST</title>
  </head>
  <body>
    <input type="text" id="message">
    <button>Send</button>
    <ul></ul>
    <script type="text/javascript">
      var ws = new WebSocket('ws://'   document.domain   ':'   location.port   '/ws');
      ws.onmessage = function (event) {
        var messages_dom = document.getElementsByTagName('ul')[0];
        var message_dom = document.createElement('li');
        var content_dom = document.createTextNode('Received: '   event.data);
        message_dom.appendChild(content_dom);
        messages_dom.appendChild(message_dom);
      };

      var button = document.getElementsByTagName('button')[0];
      button.onclick = function() {
        var content = document.getElementsByTagName('input')[0].value;
        ws.send(content);
      };


    document.addEventListener('DOMContentLoaded', function() {
    var es = new EventSource('/telepath');
    es.onmessage = function (event) {
        var messages_dom = document.getElementsByTagName('ul')[0];
        var message_dom = document.createElement('li');
        var content_dom = document.createTextNode('Received: '   event.data);
        message_dom.appendChild(content_dom);
        messages_dom.appendChild(message_dom);
      };

    
    });


    let socket = new WebSocket("ws://localhost:5000/ws");

    socket.onmessage = function(event) {
        alert(`Data received: ${event.data}`);
        
    };

    
    
    </script>
  </body>
</html>
  

Моей конечной целью будет аутентификация и нацеливание на то, кто
может получать личные сообщения с сервера.

Невозможно передать опубликованные данные через curl в клиенте Mozilla или Chrome.

Ответ №1:

В вашем collect_websocket декораторе вы передаете queue аргумент обработчику websocket ( return await func(queue, *args, **kwargs) ), тогда как ваш обработчик websocket не принимает никаких аргументов ( async def ws() ). Это приводит к ошибке, которую вы видите.

Похоже, что ваш ws_v2 обработчик websocket настроен для работы с collect_websocket декоратором ( async def ws_v2(queue) ), поэтому я думаю, вы можете просто переключиться на его использование и переписать telepath следующим образом,

 @app.route('/telepath', methods=['POST'])
async def telepath():
    global connected_websockets    
    data = await request.get_json()
    for queue in connected_websockets:
        await queue.put(data["key1"])
return {}
  

Обратите внимание, что вам не нужно создавать какие-либо очереди в /telepath маршруте, поскольку это делается вашим collect_websocket декоратором, а также поскольку вам нужна очередь для каждого соединения с websocket. Вам также не нужно ожидать ws_v2 обработчика, вместо этого он будет вызываться всякий раз, когда будет установлено новое соединение с websocket.

Для аутентификации я рекомендую вам начать с Quart-Auth (я являюсь автором библиотеки).


Редактировать: полный код по запросу,

 import asyncio
from functools import partial, wraps

from quart import (
    copy_current_websocket_context, Quart, render_template, 
    request, websocket
)

app = Quart(__name__)

connected_websockets = set()

def collect_websocket(func):
    @wraps(func)
    async def wrapper(*args, **kwargs):
        global connected_websockets
        queue = asyncio.Queue()
        connected_websockets.add(queue)
        try:
            return await func(queue, *args, **kwargs)
        finally:
            connected_websockets.remove(queue)
    return wrapper

async def broadcast(message):
    global connected_websockets 
    for queue in connected_websockets:
        await queue.put(message)

@app.route('/')
async def index():
    return await render_template('index.html')

@app.websocket('/ws')
@collect_websocket
async def ws(queue):
    await websocket.accept()
    while True:
        data = await queue.get()
        await websocket.send_json(data)

@app.route('/telepath', methods=['POST'])
async def telepath():   
    data = await request.get_json()
    await broadcast(data)
    return {}

if __name__ == '__main__':
    app.run(port=5000)
  

и шаблон,

 <!doctype html>
<html>
  <head>
    <title>My TEST</title>
  </head>
  <body>
    <ul></ul>
    <script type="text/javascript">
      var ws = new WebSocket('ws://'   document.domain   ':'   location.port   '/ws');
      ws.onmessage = function (event) {
        const messagesDOM = document.getElementsByTagName('ul')[0];
        const messageDOM = document.createElement('li');
        const message = JSON.parse(event.data).message;
        const contentDOM = document.createTextNode('Received: '   message);
        messageDOM.appendChild(contentDOM);
        messagesDOM.appendChild(messageDOM);
      };
    </script>
  </body>
</html>
  

Который затем работает с curl,

 curl -H "content-type: application/json" -d '{"message": "Hello"}' localhost:5000/telepath
  

Обратите внимание, что сервер отправит все полученные данные JSON клиенту, но клиент использует только message ключ.

Комментарии:

1. не могли бы вы привести полный подробный рабочий пример, пожалуйста, я предлагаю награду в 100 баллов 😉

2. Да, я только что добавил полный подробный рабочий пример. Это позволяет вам отправлять сообщения по маршруту / telepath, который затем отправляется подключенным клиентам.