Open bunnyi116 opened 1 year ago
顺便提一句:这不仅仅针对http,也适用于其他适配器。
mirai-api-http有多种适配器且每个适配器都可以独立存在,为了开发者更好对接mirai-api-http,需要为开发者编写一个通用请求与响应的接口(标准的传出传入的数据包),让开发者更好的管理适配器API,避免重复编写API方法。
{
"syncId": "",
"code": 0,
"msg": "",
"data": {
"sessionKey": "YourSessionKey",
"qq": {
"id": 1234567890,
"nickname": "",
"remark": ""
}
}
}
{
"syncId": "",
"data": {
"code": 0,
"session": "YourSessionKey"
}
}
syncId 我觉得服务端主动推送直接为空或null就行,不用-1配置保留字段,然后客户端请求API的时候,syncId的值长度必须大于0,然后服务端校验一下客户端的请求syncId的长度 <= 0 则服务端直接响应错误表示拒绝\syncId值不正确,避免客户端传入的syncId空或null值与服务端主动推送syncId冲突
HTTP body部分的格式和 Websocket content 部分的格式是一样的,应该不存在说需要重复实现的问题
数据格式都差不多,就是不规范,有很多不同的数据结构。
然后我说重复的是请求接口,因为http接口不同方法单独实现,在接口管理上的时候是很麻烦的,需要一个通用的请求接口。不然http ws api需要很多单独方法,然后接口管理里面又要实现适配器api调用,换句话说套娃。
不规范当然是有的,但已经发布的接口,数据格式不是说随随便便就直接改的
不同 adapter 之间请求和响应在实现上用的都是同一个实体,结构理论上都是一样的,不存在说这部分在不同 adapter 之间要重复实现。你所说的很多单独的、难以管理的方法,能不能提供一个例子出来
public interface IApi
{
void GetFriendList();
// 其他...
}
public interface IAdapter : IApi
{
AdapterMode Mode { get; }
}
public enum AdapterMode
{
Receive, Send
}
internal class HttpAdapter : IAdapter, IApi
{
public AdapterMode Mode { get; }
public void GetFriendList()
{
// http...
}
// 其他...
}
internal class WebSocketAdapter : IAdapter, IApi
{
public AdapterMode Mode { get; }
public void GetFriendList()
{
// ws...
}
// 其他...
}
internal class AdapterManager : IApi
{
private readonly List<IAdapter> adapters = new List<IAdapter>();
public void GetFriendList()
{
foreach (var adapter in adapters)
{
// 适配器模式(是否支持发送API)
if (adapter.Mode == AdapterMode.Send)
{
try
{
adapter.GetFriendList();
return;
}
catch (Exception) { }
}
}
}
// 其他...
}
这边需要适配器把全部的API编写出来,然后管理器也要全部编写出来。
如果通过一个通用的接口,让他们整合起来就方便许多,只要实现一个通用接口,然后通过请求通用的接口去统一实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MiraiBotClient;
public class RequestPacket
{
// ....
}
public class ResponsePacket
{
// ....
}
public interface IApi
{
void GetFriendList();
}
public interface IAdapter
{
AdapterMode Mode { get; }
ResponsePacket SendPacket(RequestPacket requestPacket);
}
public enum AdapterMode
{
Receive, Send
}
internal class HttpAdapter : IAdapter
{
public AdapterMode Mode { get; }
public ResponsePacket SendPacket(RequestPacket requestPacket)
{
throw new NotImplementedException();
}
}
internal class WebSocketAdapter : IAdapter
{
public AdapterMode Mode { get; }
public ResponsePacket SendPacket(RequestPacket requestPacket)
{
throw new NotImplementedException();
}
}
internal class AdapterManager
{
private readonly List<IAdapter> adapters = new List<IAdapter>();
public ResponsePacket SendPacket(RequestPacket requestPacket)
{
foreach (var adapter in adapters)
{
// 或者弄个优先级,自行设置
if (adapter.Mode == AdapterMode.Send)
{
return adapter.SendPacket(requestPacket);
}
}
}
}
internal class Api : IApi
{
AdapterManager AdapterManager { get; }
public Api(AdapterManager adapterManager)
{
AdapterManager = adapterManager;
}
public void GetFriendList()
{
var friendList = new RequestPacket();
// ....
AdapterManager.SendPacket(friendList);
}
// 其他...
}
可能比较简陋
不同接口的逻辑不同,即使提供通用接口,我认为该重复的地方还是会重复的。
不同 IAdapter
只负责到参数的解包和封包,共同抽离处理逻辑可以适当减少重复代码。
当然通用接口是可以提供的,但修改现有的请求结构可能性不大。你可以看看 general-router分支的这个功能能不能满足你的需求
原因
在编写独立的Http与WebSocket适配器的API的时候,发现需要重复写的东西很多,因为Http请求接口路径问题,得每个为他编写方法,为了能统一调用适配器API方法,为此建议制定标准数据包格式接口以便调用。至于其他的适配器我也不清楚,没用过
这样做的好处
可以减少编写适配器的接口的工作量,例:http、ws通过适配器接口去实现一个ResponsePacket SendPacket(RequestPacket packet)的方法,然后将已实现的适配器派生对象放入一个适配器管理器中的列表中,然后适配器管理器的也实现ResponsePacket SendPacket(RequestPacket packet)方法,不过这个方法是遍历适配器列表进行发送(如果适配器模式允许发送的话则发送),之后我准备实现发送消息API,那我只需要在适配器管理器(或新建一个专门实现API类)中实现一个叫做SendFriendMessage方法,然后调用适配器管理器的SendPacket即可实现不同适配器一起发送。
实现(mirai-api-http)
数据包格式
RequestPacket(请求数据包)
ResponsePacket(响应数据包)