FastAPI: websocket的用法及举例

1. Websocket

1.1 Websocket介绍

  WebSocket 是一种在单个TCP连接上进行全双工通信的协议,允许客户端和服务器之间相互发送数据,而不需要像传统的HTTP请求-响应模型那样频繁建立和断开连接。
全双工通信(Full-Duplex Communication)是一种通信模式,允许通信双方同时发送和接收数据。换句话说,数据可以同时从两端双向传输,而不会相互阻塞或干扰。

1.2 FastAPI中的Websocket

  FastAPI提供了对WebSocket的原生支持,允许你轻松构建高效的实时应用,如聊天室、实时数据更新等。

1.2.1 装饰器

  FastAPI中与WebSocket相关的主要装饰器为 @app.websocket。该装饰器的作用和参数如下:

  • 作用:将一个路径(如/ws)与一个处理WebSocket请求的函数关联。当客户端通过WebSocket连接该路径时,FastAPI会调用该函数处理连接和通信。
  • 参数:它接受的参数与其他路由装饰器相同,主要是路径(URL),可选地也能设置依赖项、权限等。

代码举例如下(当客户端通过WebSocket连接/ws路径时,FastAPI将执行下面的websocket_endpoint函数):

from fastapi import FastAPI, WebSocket
app = FastAPI()
# 定义一个 WebSocket 路由
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()  # 接受 WebSocket 连接
    while True:
        data = await websocket.receive_text()  # 接收来自客户端的消息
        await websocket.send_text(f"Message text was: {data}")  # 回复消息给客户端
1.2.2 websocket相关方法

  FastAPI提供了处理WebSocket各种事件的方法,包括接受消息、发送消息、关闭连接等。具体如下:

  • websocket.accept:接受WebSocket连接请求。
  • websocket.receive_text:接收客户端发来的文本消息。
  • websocket.send_text:向客户端发送文本消息。
  • websocket.close:关闭WebSocket连接。

2. 构建对话机器人

  这里我们用FastAPI和React构建一个聊天机器人的聊天界面。这里关于机器人的后端处理逻辑这里不做详细介绍,而前端部分主要介绍App.tsx和ChatPag.tsx内容,不介绍CSS部分。具体代码如下:
React中App.tsx代码如下:

import './App.css';
import ChatPage from './components/ChatPage';

function App() {
  return (
    <div className="App">
        <div className="header">
          <div className="header-logo">
            <img src="https://cdn.builder.io/api/v1/image/assets/TEMP/b0db057162d379f22892cd5ae4d13c509717e0a81da39be3f65cb94e15556ed7?apiKey=0682bce60b3549f085131079f1bf89f0&&apiKey=0682bce60b3549f085131079f1bf89f0" alt="Chainlit" /> 
            <div className="header-title">SmartRecommend服务推荐助手</div>
          </div>
        </div>
        <div className='body-container'>
          <div className="main">
            <div className='chatpage'>
              <ChatPage />
            </div>
          </div>
        </div>
      </div>
  );
}
export default App;

React中ChatPage.tsx代码如下:

import "./ChatPage.css";
import { useEffect, useState} from "react";
import { nanoid } from 'nanoid';

interface Message{
    id:string,
    name:string,
    type:string,
    output:string,
    createdAt:number|string,
}
function ChatPage() {
    const [inputValue, setInputValue] = useState("");
    const [messages,setMessages] = useState<Message[]>([]);
    const [socket,setSocket] = useState<WebSocket|null>(null);
    
    useEffect(() => {
        const ws = new WebSocket("ws://localhost:8000/ws/chat");
        ws.onopen = () => {
          console.log("websocket链接已建立!");
        };
        ws.onmessage = (event) => {
          const message = JSON.parse(event.data);
          setMessages((prevMessages) => [...prevMessages, message]);
        };
        ws.onerror = (error) => {
            console.log('WebSocket错误:', error);
        };
        ws.onclose=()=>{
            console.log("websocket链接已关闭!");
        }
        setSocket(ws);
        return () => {
            ws.close();
        }
    },[]);

    const handleSendMessage = () => {
        const content = inputValue.trim();
        if (content) {
          const message: Message={
            id: nanoid(),
            name: "User",
            type: "user_message",
            output: content,
            createdAt: Date.now(),
          };
        setMessages((prevMessages) => [...prevMessages, message]);
        socket?.send(JSON.stringify(message));
        }
        setInputValue("");
      };

    const renderMessage = (message:Message,index:number) => {
        const dateOptions: Intl.DateTimeFormatOptions = {
            hour: "2-digit",
            minute: "2-digit",
          };
          const date = new Date(message.createdAt).toLocaleTimeString(
            undefined,
            dateOptions
          );
          if(message.type === "user_message") {
            return (
                <div key={message.id} className="chat-box-user">
                  <div className="user-avatar">U</div>
                  <div className="bot-user-content">
                    <div className="user-icon">
                      <div className="bot-user-name">{message.name}</div>
                      <div className="bot-user-time">{date}</div>
                    </div>
                    <div className="user-chat-message">{message.output}</div>
                  </div>
                </div>
            );
          } else {
              return (
                <div key={message.id} className="chat-box-bot">
                  <div className="bot-avatar">B</div>
                  <div className="bot-user-content">
                    <div className="bot-icon">
                        <div className="bot-user-name">{message.name}</div>
                        <div className="bot-user-time">{date}</div>
                    </div>
                    <div className="bot-chat-message">{message.output}</div>
                  </div>
                </div>
              );
          };
        };
    return (
        <div className="chat-container">
        <div className="chat-box">
            {messages.map(renderMessage)}
        </div>
        <div className="fixed-bottom">
          <input className="fixed-bottom-input" 
                type="text"
                value={inputValue}
                placeholder="你可以输入“你好”唤醒服务"
                onChange={(e) => setInputValue(e.target.value)}
                onKeyUp={(e) => {
                  if (e.key === "Enter") {
                    handleSendMessage();
                  }
                }}>
          </input>
          <button onClick={handleSendMessage} className="button" type="submit">Send</button>
        </div>
      </div>
    ); 
}
export default ChatPage;

后端FastAPI代码:

from fastapi import FastAPI, WebSocket,HTTPException
import uvicorn
from fastapi.middleware.cors import CORSMiddleware
from typing import List
import json
import datetime
from nanoid import generate
import httpx

app=FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)
clients: List[WebSocket] = []

RASA_API_URL="http://localhost:5005/webhooks/rest/webhook"

@app.websocket("/ws/chat")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    clients.append(websocket)
    try:
        while True:
            data = await websocket.receive_text()
            for client in clients:
                text={"id": generate(),
                      "name":"Bot","type":"bot_message",
                      "output":json.loads(data)["output"],
                      "createdAt":int(datetime.datetime.now().timestamp()*1000)}
                text=json.dumps(text)
                await client.send_text(text)
    except Exception as e:
        print(e)
        clients.remove(websocket)
        
if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)
Python FastAPI 是一个高性能、易于使用、快速编写 API 的 Web 框架。如果你想要在 FastAPI 中增加 WebSocket,可以使用第三方库 fastapi-websocket 来实现。 首先,你需要在你的项目中安装 fastapi-websocket 库。你可以使用 pip 命令进行安装: ``` pip install fastapi-websocket ``` 安装完成后,在你的 FastAPI 项目中导入 fastapi_websocket 包。然后,你可以通过创建一个 WebSocketEndpoint 类来实现 WebSocket: ```python from fastapi import FastAPI from fastapi_websocket import WebSocket app = FastAPI() class WebSocketEndpoint: def __init__(self, ws: WebSocket): self.ws = ws async def send_message(self, message: str): await self.ws.send_text(message) @app.websocket("/ws") async def websocket_endpoint(ws: WebSocket): websocket = WebSocketEndpoint(ws) await websocket.send_message("Hello, WebSocket!") ``` 在上面的代码中,我们创建了一个 WebSocketEndpoint 类来实现 WebSocket。这个类中包含了一个 send_message 方法,用来发送消息给客户端。然后,在 websocket_endpoint 函数中,我们创建了一个 WebSocketEndpoint 对象,并通过 send_message 方法发送一条消息。 最后,我们将这个 WebSocketEndpoint 函数装饰为 /ws 路径的 WebSocket: ```python @app.websocket("/ws") async def websocket_endpoint(ws: WebSocket): websocket = WebSocketEndpoint(ws) await websocket.send_message("Hello, WebSocket!") ``` 如果你想要了解更多关于 FastAPIfastapi-websocket 的内容,可以查看官方文档:https://fastapi.tiangolo.com/tutorial/websockets/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值