尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

djangochannelsrestframework 消息协议详解:action 与 request_id 如何驱动 WebSocket 双向通信

djangochannelsrestframework 消息协议详解:action 与 request_id 如何驱动 WebSocket 双向通信 djangochannelsrestframework 消息协议详解action 与 request_id 如何驱动 WebSocket 双向通信【免费下载链接】djangochannelsrestframeworkA Rest-framework for websockets using Django channels-v4项目地址: https://gitcode.com/gh_mirrors/dj/djangochannelsrestframeworkdjangochannelsrestframework简称 DCRF是一个基于 Django Channels v4 的 WebSocket REST 框架它把 DRFDjango REST Framework中熟悉的接口风格搬到了 WebSocket 之上。本文将从消息协议入手用最通俗的方式拆解djangochannelsrestframework 消息协议中两个最核心的字段action动作与request_id请求标识带你看懂它们如何像地址和快递单号一样驱动起 WebSocket 的双向通信。无论你是刚接触 WebSocket 的新手还是想深入源码的进阶用户这篇文章都能帮你快速建立完整的协议认知。什么是 djangochannelsrestframework 消息协议在传统 HTTP 世界里一次请求对应一次响应结构天然清晰。但 WebSocket 是一条长连接客户端和服务端可以在任意时刻互相发消息那么问题来了服务端怎么知道客户端想干什么多条消息并发时客户端怎么知道哪条响应对应哪条请求djangochannelsrestframework 消息协议就是为此设计的答案。它规定了统一的消息外壳客户端发出去的每条消息都包含action和request_id服务端返回的每条响应也都原样带上这两个字段。核心实现就在项目源码的 djangochannelsrestframework/consumers.py 中——AsyncAPIConsumer类是整个协议的中枢。一条最典型的客户端消息长这样{ action: list, request_id: 150060530 }对应服务端的响应{ action: list, errors: [], response_status: 200, request_id: 150060530, data: [{id: 1, username: test1}, {id: 2, username: test2}] }是不是感觉和 HTTP 的URL 状态码 响应体很像没错action 就是URLresponse_status 就是状态码request_id 则是让这一切可以异步对号入座的快递单号。action 字段WebSocket 消息的路由入口 action 的作用只有一个告诉服务端你想调用哪个功能。它相当于 RESTful API 里的端点endpoint但通过 WebSocket 传输。消息是如何被路由的当你通过 WebSocket 发来一条 JSON 消息时服务端的处理流水线是这样的见 consumers.py 的receive_json方法先从消息中取出request_id再通过get_action_name方法取出action字段交给handle_action进行权限校验和分发找到对应的方法执行并把结果通过reply方法回传。如果消息里没有 action 字段服务端会抛出ActionMissingException定义在 djangochannelsrestframework/exceptions.py并返回 405 状态码的错误响应——这就像访问了一个不存在的 URL 一样。内置 action 一览开箱即用的 CRUDDCRF 在 djangochannelsrestframework/mixins.py 中内置了一整套类 DRF 的 action几乎覆盖了日常 80% 的需求action对应功能类似 HTTP 方法典型响应码create新建数据POST201list列表查询GET200retrieve单条详情GET200update整体更新PUT200patch局部更新PATCH200delete删除数据DELETE204比如前端要创建用户只需发送{ action: create, request_id: new Date().getTime(), data: {username: test, password: 123456} }服务端CreateModelMixin.create方法会完成序列化校验并落库然后返回 201 和新数据的完整序列化结果。整个过程对前端来说就是发一条消息、收一条消息非常直观。自定义 action用 action 装饰器扩展协议内置 action 不够用你完全可以自定义。在 djangochannelsrestframework/decorators.py 中action()装饰器会把任意方法登记进消费者的 action 路由表available_actions这一步由APIConsumerMetaclass元类在类创建时自动完成。from djangochannelsrestframework.decorators import action class MyConsumer(AsyncAPIConsumer): action() async def delete_user(self, request_id, user_pk, **kwargs): ...之后前端发送{action: delete_user, request_id: 42, user_pk: 82}即可触发。这个装饰器还支持两个高级选项action(atomicTrue)用于同步方法让操作包在数据库事务中action(detachedTrue)用于异步方法让耗时操作如请求外部 API脱离主循环运行期间连接仍能处理其他消息。request_id 字段让异步应答对号入座 如果说 action 是地址那request_id 就是快递单号。它是 djangochannelsrestframework 消息协议中最容易被新手忽略、却最关键的字段。为什么需要 request_idWebSocket 是异步的客户端可以连续发送多条消息服务端处理耗时各不相同返回顺序完全无法保证。如果没有 request_id客户端收到响应时根本不知道这是哪条请求的结果。而 DCRF 的约定是服务端返回的每条响应都会原样携带触发它的 request_id。前端收到响应后只需比对 request_id 就能把异步响应准确关联到对应请求上ws.send(JSON.stringify({ action: list, request_id: 1 })); ws.send(JSON.stringify({ action: create, request_id: 2, data: {...} })); // 响应即使乱序到达也能靠 request_id 区分归属在 consumers.py 的reply方法中可以看到响应的固定结构就是errors / data / action / response_status / request_id五个字段其中 request_id 的设计初衷文档注释里写得很明白include the request_id if possible as this helps clients link messages they have sent to responses尽量带上 request_id它帮助客户端把收到的消息与发出的消息对应起来。观察者模式下 request_id 的高级玩法request_id 的价值在观察者Observer模式下体现得淋漓尽致。DCRF 的 Observer 机制允许客户端订阅某个数据源的变化一旦数据变化服务端会主动推送消息——这就是真正的服务端 → 客户端推送。在 djangochannelsrestframework/observer/base_observer.py 的subscribe/unsubscribe方法中request_id 被用来跟踪订阅关系当你用某个 request_id 订阅后服务端会把 request_id 记录在_observer_group_to_request_id映射表中当订阅的数据发生变化时服务端就能精确地把更新推送给发起了那次订阅请求的连接甚至能把同一个数据源的多个订阅请求合并成一次推送大大节省带宽。一次完整的双向通信请求与响应拆解 把前面的知识串起来一次完整的 djangochannelsrestframework 双向通信是这样的连接建立客户端通过ws://localhost:8000/ws/建立 WebSocket 连接期间会执行websocket_connect中的权限校验发起请求客户端发送{action: retrieve, request_id: 42, pk: 1}请求单条数据权限与路由服务端在handle_action中先做权限检查再根据 action 找到retrieve方法执行返回响应执行结果通过reply打包成统一协议格式返回携带相同的 request_id持续通信连接保持打开双方可继续按同样协议收发消息直到某一方关闭连接。值得一提的还有 djangochannelsrestframework/consumers.py 中的DjangoViewAsConsumer和配套的view_as_consumer工具——它能把一个普通 Django View 直接包装成 WebSocket 消费者让 action 映射到 HTTP 方法如create→PUT、list→GET实现老代码零改造上 WebSocket的平滑迁移。异常与错误处理的协议约定 ⚠️理解消息协议错误处理同样重要。DCRF 的异常处理遵循统一约定见handle_exception方法权限不足抛出PermissionDenied返回 403action 不存在抛出MethodNotAllowed返回 405资源找不到抛出Http404返回 404校验失败序列化器的is_valid(raise_exceptionTrue)抛出 DRF 的APIException错误的详细信息会以列表形式放入errors字段返回。也就是说无论成功还是失败响应永远保持同一种协议结构前端只需解析response_status和errors两个字段就能统一处理所有场景无需为异常单独设计协议分支。常见问题与最佳实践 最后给初学者的几条实用建议request_id 必须唯一建议用时间戳、UUID 或自增序号别用固定值否则多请求并发时无法区分响应action 命名保持语义化遵循 REST 风格create/list/retrieve/update/delete自定义 action 也尽量用动词名词善用订阅式推送需要实时刷新数据的场景如聊天、通知、在线列表优先使用 Observer 的订阅机制而不是频繁轮询权限校验别遗漏每个 action 都会经过check_permissions自定义 action 记得配置好对应的权限类关注 detached 任务使用detachedTrue的长任务会在连接关闭时被自动取消注意在任务中妥善处理取消异常。总结 djangochannelsrestframework 消息协议的设计精髓就是用 action 定义做什么用 request_id 关联哪次请求。理解了这两个字段你就掌握了 WebSocket 双向通信的钥匙既能实现类 REST 的请求-响应模式也能通过订阅机制实现服务端主动推送。配合action装饰器、内置 CRUD mixins 和 Observer 观察者你可以用极少的代码搭建出功能完整的实时 API——这正是 djangochannelsrestframework 作为WebSocket 版 DRF的核心价值。【免费下载链接】djangochannelsrestframeworkA Rest-framework for websockets using Django channels-v4项目地址: https://gitcode.com/gh_mirrors/dj/djangochannelsrestframework创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表