SignalR Core与.NET Core:如何实现契约校验并捕获参数错误?
SignalR TypeScript客户端契约校验与错误抛出方案
1. 生成强类型客户端(根源解决)
SignalR支持从.NET服务端Hub代码直接生成TypeScript强类型客户端,编译阶段就能拦截方法名错误、参数不匹配问题,完全不用等到运行时调试。
操作步骤:
- 给服务端项目安装NuGet包
Microsoft.AspNetCore.SignalR.TypeScript - 在项目的
.csproj文件中添加生成配置,指定Hub文件路径和输出的TypeScript文件位置:
<ItemGroup> <SignalRClient Include="Hubs/**/*.cs" /> </ItemGroup> <Target Name="GenerateSignalRClient" AfterTargets="Build"> <Exec Command="dotnet tool restore" /> <Exec Command="dotnet signalr generate client -i $(ProjectDir)/Hubs/ChatHub.cs -o $(ProjectDir)/../Client/src/signalr-hub.ts" /> </Target>
- 编译服务端项目后,会自动生成包含所有Hub方法强类型定义的TS文件,调用时只要方法名写错、参数类型/数量不对,VS Code会直接标红,编译也通不过。
2. 手动封装调用方法,添加前置校验
如果暂时不想生成强类型代码,自己封装一层invoke方法,提前做方法名和参数校验:
import { HubConnection } from "@microsoft/signalr"; class TypedHub { private conn: HubConnection; // 维护服务端真实存在的方法及所需参数数量 private validMethods: Record<string, number> = { "SendMessage": 2, "JoinGroup": 1 }; constructor(connection: HubConnection) { this.conn = connection; } async invoke<T>(methodName: string, ...args: any[]): Promise<T> { // 校验方法是否存在 if (!this.validMethods[methodName]) { throw new Error(`SignalR方法「${methodName}」不存在`); } // 校验参数数量 if (args.length !== this.validMethods[methodName]) { throw new Error(`方法「${methodName}」需要${this.validMethods[methodName]}个参数,实际传了${args.length}个`); } // 调用原方法并增强错误信息 try { return await this.conn.invoke(methodName, ...args); } catch (err) { throw new Error(`调用「${methodName}」失败: ${(err as Error).message}`); } } } // 使用示例 // const rawConn = new HubConnectionBuilder().withUrl("/chatHub").build(); // const hub = new TypedHub(rawConn); // hub.invoke("SendMessage", "test") // 参数数量不对,直接抛出错误
3. 服务端主动校验并抛出异常
在服务端Hub里做参数校验,同时处理未定义的方法调用,主动抛出HubException,客户端就能通过catch捕获到错误:
服务端C#代码:
using Microsoft.AspNetCore.SignalR; using System.Reflection; public class ChatHub : Hub { private static readonly HashSet<string> _validMethods = typeof(ChatHub) .GetMethods(BindingFlags.Public | BindingFlags.Instance) .Where(m => m.ReturnType.IsAssignableTo(typeof(Task))) .Select(m => m.Name) .ToHashSet(); public async Task SendMessage(string user, string message) { if (string.IsNullOrWhiteSpace(user) || string.IsNullOrWhiteSpace(message)) { throw new HubException("用户名和消息不能为空"); } await Clients.All.SendAsync("ReceiveMessage", user, message); } // 拦截所有方法调用,校验方法是否存在 public override Task OnInvokeAsync(string methodName, object[] args, CancellationToken cancellationToken) { if (!_validMethods.Contains(methodName)) { throw new HubException($"方法「${methodName}」不存在于服务端"); } return base.OnInvokeAsync(methodName, args, cancellationToken); } }
客户端调用示例:
try { await connection.invoke("aaaa"); // 服务端抛出异常,客户端catch能捕获 } catch (err) { console.error("调用失败:", err); }
4. 用JSON Schema做参数结构校验
如果需要更复杂的参数结构校验,可以结合JSON Schema工具(比如Ajv)在客户端调用前验证参数:
import Ajv from "ajv"; const ajv = new Ajv(); // 定义SendMessage的参数校验规则 const sendMessageSchema = { type: "array", items: [ { type: "string", minLength: 1 }, { type: "string", minLength: 1 } ] }; const validateSendMessage = ajv.compile(sendMessageSchema); async function sendMessage(user: string, message: string) { const args = [user, message]; if (!validateSendMessage(args)) { throw new Error("参数不符合要求: " + JSON.stringify(validateSendMessage.errors)); } await connection.invoke("SendMessage", ...args); }
内容的提问来源于stack exchange,提问作者Benyamin Limanto
相关产品推荐
相关产品推荐

