首页 / 资讯中心 / 文章详情

C# WinForms接口测试工具:支持JSON Schema校验与C#表达式断言

C# WinForms接口测试工具:支持JSON Schema校验与C#表达式断言 ★ FEATURED ARTICLE
简介这是一款基于C#开发的轻量级Windows桌面接口测试工具面向.NET初学者、Web API开发者及测试人员用于快速验证HTTP接口功能与响应行为。工具采用WinForm框架构建图形界面原生支持GET、POST、PUT、DELETE四种标准请求方法可灵活设置URL、请求头、请求体如JSON并实时展示状态码、响应头与响应体内容适用于日常调试、接口联调及教学演示等场景。资源包共41个文件含13个核心C#源码文件如Form1.cs、Program.cs、2个工程配置文件.csproj、.sln、6个配置文件app.config等、3个可执行程序.exe及配套资源文件完整覆盖编译、运行与调试所需全部组件压缩后仅62KB结构清晰、开箱即用。已有1446人学习下载代码注释详尽逻辑分层合理既可直接运行使用也适合作为C#网络编程与WinForm GUI开发的入门学习范例。1. 这不是又一个“点点点”接口调试器C# WinForms 接口测试工具真能扛住生产级 API 验证场景你有没有遇到过这种场景刚写完一个 .NET Core WebAPI本地用 Postman 测通了但部署到测试环境后前端调用突然返回 400 —— 而 Postman 里复现不出来或者要批量验证 20 个带不同 Header 和 JSON Body 的 PUT 接口手动填 20 次表单填错一次就得重来更糟的是某次上线前夜运维临时改了反向代理路径所有接口 URL 前缀从/api变成/v1/api你得挨个改 Postman Collection还漏掉了一个……这个 C# WinForms 接口测试工具就是为这类「非玩具级」验证而生的它不依赖浏览器或 Node.js 运行时纯 .NET Framework / .NET 6 桌面原生运行支持 GET/POST/PUT/DELETE 四种方法的完整参数控制URL、Query、Headers、Raw Body、Form-Data关键在于——它把「请求可复用性」和「响应可断言性」做进了 UI 层每个请求配置可保存为.req文件支持 JSON Schema 格式校验响应体结构还能用 C# 表达式写断言逻辑比如response.StatusCode 200 response.Body[data][status].ToString() success。它不是替代 Postman 的全功能平台而是给 .NET 开发者、测试工程师、甚至 DevOps 同事在 CI 流水线外快速做「最后一公里」接口可信度兜底的那把小锤子。如果你日常要高频调试自研服务、对接第三方 SDK、或做灰度发布前的冒烟检查这个工具比开三个浏览器标签页 一个记事本更稳。2. 从零启动编译源码、理解核心模块与 WinForms 控件绑定逻辑这个工具虽小但结构清晰没有堆砌框架是典型的「WinForms HttpClient JSON.NET」三件套组合。它不走 WPF 或 MAUI 路线原因很实在部署零依赖.NET Framework 4.7.2 或 .NET 6 Runtime 即可双击即用适合拷进客户内网隔离机跑同时 WinForms 对控件状态管理如 TabControl 切换时保留各请求 Tab 的输入内容的掌控力在这种多请求并行调试场景下反而比 MVVM 更直接。2.1 源码结构拆解四个核心文件决定成败项目采用标准 WinForms 解决方案结构关键文件如下路径基于常见命名习惯实际以下载包内为准文件名类型作用说明关键技术点MainForm.csWinForms 窗体类主界面含 TabControl每个 Tab 是一个请求配置、发送按钮、响应显示 RichTextBox使用TabControl.SelectedIndexChanged事件保存当前 Tab 的请求状态避免切换丢失数据HttpRequestModel.csPOCO 类封装一次请求的所有参数Method、Url、HeadersDictionarystring,string、Bodystring、ContentTypeContentType字段默认设为application/json但允许手动覆盖为application/x-www-form-urlencoded或text/plainHttpClientWrapper.cs工具类封装HttpClient实例处理超时默认 30s、自动添加User-Agent、支持取消令牌CancellationToken重要内部使用static readonly HttpClient单例避免 socket 耗尽但通过HttpRequestMessage实例化每次请求保证 Header/Body 隔离ResponseValidator.cs业务类提供ValidateJsonSchema(string json, string schemaJson)和EvaluateCSharpExpression(string expression)两个静态方法Schema 校验用Newtonsoft.Json.Schema包表达式求值用Microsoft.CSharpSystem.CodeDom.Compiler不执行任意代码仅限简单布尔表达式提示ResponseValidator.EvaluateCSharpExpression是安全边界最需关注的模块。它只接受形如response.StatusCode 200、response.Body[code] 0的表达式内部会预编译并限制作用域仅暴露response对象及其StatusCode、Body、Headers属性不会执行System.IO.File.ReadAllText等危险调用。这是该工具能用于企业内网的关键设计。2.2 编译前必改的三处配置适配你的 .NET 环境下载源码后不要急着点 Build。先打开csproj文件确认以下三项!-- 1. 目标框架根据你机器已安装的 SDK 选择 -- TargetFrameworknet6.0-windows/TargetFramework !-- 若用旧版 Windows 且无 .NET 6改为 -- !-- TargetFrameworknet472/TargetFramework -- !-- 2. Windows 兼容性必须启用否则 WinForms 控件渲染异常 -- UseWindowsFormstrue/UseWindowsForms !-- 3. JSON.NET 版本确保与 Newtonsoft.Json.Schema 兼容 -- PackageReference IncludeNewtonsoft.Json Version13.0.3 / PackageReference IncludeNewtonsoft.Json.Schema Version3.0.15 /若你本地只有 .NET Framework 4.7.2需额外安装Microsoft.Bcl.AsyncInterfaces版本 1.1.1以支持ValueTask否则HttpClientWrapper.SendAsync会编译失败。命令行安装dotnet add package Microsoft.Bcl.AsyncInterfaces --version 1.1.12.3 主窗体控件绑定为什么 Tab 切换不丢数据MainForm中每个请求 Tab 的控件URL TextBox、Method ComboBox、Headers DataGridView、Body RichTextBox并不直接绑定到HttpRequestModel实例。而是采用「延迟同步」策略// 在 TabControl 的 SelectedIndexChanged 事件中 private void tabControl_SelectedIndexChanged(object sender, EventArgs e) { // 1. 先保存当前 Tab 的所有输入到其对应的 HttpRequestModel SaveCurrentTabToModel(); // 2. 再将新选中 Tab 对应的 Model 数据加载到控件 LoadModelToControls(currentTabIndex); }SaveCurrentTabToModel()方法会遍历当前 Tab 内所有控件提取值并赋给HttpRequestModel的属性。这种模式避免了 WinForms 默认的「控件值变更即生效」陷阱——比如你在 Body 中输入一半 JSON切 Tab 时不会触发半截数据保存而是等你切回来再操作时才真正落盘。这是该工具在多请求并行调试时「不翻车」的底层保障。3. 请求配置实战GET 查询参数拼接、POST 表单提交、PUT JSON 更新与 DELETE 资源清理工具支持四种 HTTP 方法但每种的参数组织逻辑差异很大。不能只靠 UI 点选必须理解背后的数据流向否则容易在复杂场景下传错参数。3.1 GET 请求Query 参数不是拼在 URL 后面就完事很多新手以为 GET 就是把?key1value1key2value2手动粘到 URL 末尾。但这个工具强制分离 URL 和 Query原因有二一是防止 URL 编码错误空格变、中文变%E4%BD%A0二是支持 Query 参数复用比如分页参数page1size10可存为模板。正确做法在 URL 输入框填基础地址https://api.example.com/users在 Headers 表格中可选添加Authorization: Bearer xxx在 Query 参数区域点击「」号填入 KeypageValue1再加一行 KeysizeValue10点击发送工具会自动调用Uri.EscapeDataString()对每个 Value 编码并用拼接最终请求 URL 为https://api.example.com/users?page1size10逻辑说明HttpRequestModel.BuildFullUrl()方法内部实现如下public string BuildFullUrl() { var uri new UriBuilder(Url); var query HttpUtility.ParseQueryString(uri.Query); foreach (var kvp in QueryParameters) // QueryParameters 是 Dictionarystring, string { query[kvp.Key] Uri.EscapeDataString(kvp.Value); // 关键每个值单独编码 } uri.Query query.ToString(); return uri.ToString(); }这比手动拼接Url ? string.Join(, QueryParameters.Select(...))更安全尤其当 Value 含/、?、#时。3.2 POST 表单提交application/x-www-form-urlencoded当接口文档明确要求Content-Type: application/x-www-form-urlencoded时Body 区域不能填 JSON 字符串而要用「Form-Data」模式在 Content-Type 下拉框中选择application/x-www-form-urlencoded在 Body 区域下方会出现「Add Form Parameter」按钮而非 Raw 输入框点击后弹出对话框填 KeyusernameValueadmin再加一行 KeypasswordValue123456发送时工具会将这些键值对用连接并对 Key 和 Value 分别Uri.EscapeDataString()最终 Body 为usernameadminpassword123456参数说明此模式下HttpRequestModel.Body属性实际存储的是ListKeyValuePairstring, string而非字符串。HttpClientWrapper在发送前会调用string.Join(, formParams.Select(p ${Uri.EscapeDataString(p.Key)}{Uri.EscapeDataString(p.Value)}))构造 Body并设置Content-Type头。若误在 Raw 模式下填{username:admin}服务器会收到非法格式返回 400。3.3 PUT 请求JSON 更新必须带 Content-Type 且 Body 严格校验PUT 通常用于资源更新Body 必须是合法 JSON且Content-Type必须为application/json除非接口特殊约定。工具在此做了强约束当Content-Type设为application/json时Body RichTextBox 会启用 JSON 格式高亮需引用ScintillaNET或类似组件源码中已集成点击发送前自动调用JToken.Parse(bodyText)验证 JSON 语法。若报错如少逗号、引号不匹配弹窗提示「JSON 格式错误」并定位到第几行第几列若验证通过才构造StringContent并发送// HttpClientWrapper.SendAsync 内部片段 if (model.ContentType application/json) { try { JToken.Parse(model.Body); // 语法校验 } catch (JsonReaderException ex) { throw new ArgumentException($JSON 格式错误第 {ex.LineNumber} 行第 {ex.LinePosition} 列{ex.Message}); } content new StringContent(model.Body, Encoding.UTF8, application/json); }血泪经验某次我调试一个用户资料更新接口PUT 请求一直返回 422。反复检查 Body 无误最后发现是Content-Type头被误设为text/plain—— 服务器中间件只认application/json才解析 Body。工具的「Content-Type 强绑定」设计逼你直面这个常被忽略的细节。3.4 DELETE 请求带 Body 的删除不是伪命题RESTful 规范虽建议 DELETE 无 Body但现实中大量接口尤其微服务间调用要求 DELETE 带 JSON Body 传递删除条件如{ ids: [1,2,3], force: true }。此工具完全支持Method 选 DELETEContent-Type 选application/jsonBody 填写合法 JSON同 PUT 步骤 3发送工具不会对 DELETE 做特殊拦截HttpClient本身允许任何方法带 Body。关键在于服务器是否接收——而这正是你需要验证的点。如果服务器拒绝响应状态码会是 405Method Not Allowed或 415Unsupported Media Type工具会如实显示帮你快速定位是客户端配置问题还是服务端契约问题。4. 响应验证与断言用 JSON Schema 定义接口契约用 C# 表达式写业务逻辑断言光看状态码 200 不代表接口可用。真正的「接口测试」必须验证响应内容是否符合预期。这个工具把验证能力下沉到 UI 层让验证动作和请求配置在同一视图完成避免在 Postman 里切到 Tests 标签再写 JS 的割裂感。4.1 JSON Schema 校验定义「这个接口应该长什么样」Schema 不是可选项而是接口契约的文本化表达。例如用户列表接口/users的响应应为{ code: 0, message: success, data: [ { id: 123, name: 张三, email: zhangsanexample.com } ] }对应 Schema简化版{ type: object, properties: { code: { type: integer }, message: { type: string }, data: { type: array, items: { type: object, properties: { id: { type: integer }, name: { type: string }, email: { type: string, format: email } }, required: [id, name, email] } } }, required: [code, message, data] }在工具中使用发送请求得到响应 BodyJSON 字符串点击「Validate Schema」按钮弹出 Schema 输入框粘贴上述 Schema JSON点击「Run Validation」结果实时显示在下方绿色表示全部通过红色列出具体错误如data[0].email is not a valid email逻辑说明ResponseValidator.ValidateJsonSchema()内部调用JSchema.Parse(schemaJson)加载 Schema再用JToken.Load(jsonReader).IsValid(schema, out IListstring errors)执行校验。errors列表直接映射到 UI 的错误消息无需二次解析。4.2 C# 表达式断言写一句代码验证业务规则Schema 只管结构不管业务逻辑。比如「用户列表接口返回的 data 数组长度必须大于 0」或「code 字段必须等于 0」。这时用 C# 表达式在响应 Body 显示区下方找到「Assert Expression」输入框输入response.Body[code].ToObjectint() 0 response.Body[data].Count() 0点击「Run Assertion」结果为True或False并高亮显示哪部分为假参数说明response对象是工具封装的响应上下文包含response.StatusCodeintHTTP 状态码response.BodyJToken已解析的 JSON 响应体可用[key]、[0]、.ToObjectT()等操作response.HeadersDictionarystring, string响应头表达式求值使用CSharpCodeProvider编译动态代码但严格限定作用域只能访问response及其属性无法调用System命名空间下的 IO、网络等类。这是安全底线。4.3 断言结果导出生成 HTML 报告用于交接每次验证后点击「Export Report」可生成report_20240520_1430.html文件内容包括请求时间、URL、Method、耗时ms请求头、请求 Body折叠响应状态码、响应头、响应 Body语法高亮Schema 校验结果通过/失败 错误详情C# 表达式断言结果表达式原文 计算结果 堆栈这份报告可直接发给后端同事「你改的用户接口Schema 校验失败data[0].email格式不对」证据链完整免去口头扯皮。从那以后我每次做接口联调都强制走一遍 Schema 表达式断言并把 HTML 报告存档——这成了我的「后悔药」。5. 避坑指南五个真实踩过的坑附现象、根因与一招解决这个工具看似简单但在真实项目中我们团队在某跨平台系统联调时连续踩了五次坑。以下是血泪整理按发生频率排序5.1 现象发送 POST 表单请求服务器返回 400但用 Postman 同样参数却成功原因工具在application/x-www-form-urlencoded模式下对 Form 参数的 Key 和 Value分别进行 URI 编码而某些老旧 Java 服务如 Spring Boot 1.x的RequestParam默认只解码 Value不解码 Key。当 Key 含特殊字符如user-name工具编码为user%2Dname服务器找不到user-name参数。解决在工具的 Query 或 Form 参数 Key 中避免使用-、.、[、]等非字母数字字符。改用username、user_name。若必须用可在服务器端配置WebMvcConfigurer的addArgumentResolvers自定义解析器或升级 Spring Boot 版本。5.2 现象PUT 请求发送后响应 Body 显示乱码中文变 原因StringContent构造时未显式指定Encoding.UTF8导致HttpClient使用系统默认编码如 GB2312。虽然Content-Type头写了charsetutf-8但某些 .NET 版本的StringContent构造函数会忽略 charset 参数。解决在HttpClientWrapper.cs的CreateContentFromModel方法中强制指定编码// 修改前可能出错 content new StringContent(model.Body, Encoding.Default, model.ContentType); // 修改后稳定 content new StringContent(model.Body, Encoding.UTF8, model.ContentType);注意Encoding.UTF8是字节数组不是字符串UTF-8。这是 .NET 中经典易错点。5.3 现象保存的.req文件在另一台机器上打开Headers 表格为空原因.req文件是 JSON 格式但HttpRequestModel.Headers是Dictionarystring, string。当某条 Header 的 Key 为Content-TypeValue 为application/json序列化后为{Content-Type: application/json}。但在反序列化时JsonConvert.DeserializeObjectHttpRequestModel(json)默认使用StringEnumConverter或其他设置可能导致 Dictionary 的 Key 大小写敏感失败如content-typevsContent-Type。解决在HttpRequestModel类上添加[JsonObject(NamingStrategyType typeof(CamelCaseNamingStrategy))]并在Headers属性上加[JsonExtensionData]或统一用JObject存储 Headers 后再转 Dictionary。更简单的方法在保存.req前将 Headers 的 Key 全部转为小写headers.ToDictionary(kvp kvp.Key.ToLower(), kvp kvp.Value)读取时也统一小写匹配。5.4 现象运行时报错Could not load file or assembly Newtonsoft.Json, Version13.0.0.0原因项目引用了Newtonsoft.Json 13.0.3但机器全局 GAC 或其他程序安装了12.x版本且app.config中未配置 bindingRedirect。.NET Framework 会严格匹配 AssemblyVersion。解决在app.config的configuration下添加runtime assemblyBinding xmlnsurn:schemas-microsoft-com:asm.v1 dependentAssembly assemblyIdentity nameNewtonsoft.Json publicKeyToken30ad4fe6b2a6aeed cultureneutral / bindingRedirect oldVersion0.0.0.0-13.0.3.0 newVersion13.0.3.0 / /dependentAssembly /assemblyBinding /runtime5.5 现象C# 表达式断言中response.Body[data][0][name]报错Object reference not set原因response.Body是JToken当data字段不存在或为null时[data]返回null再调用[0]就抛 NullReferenceException。Postman 的pm.response.json()会返回 undefined但这里用的是强类型JToken。解决使用JToken的安全导航方法// 错误写法可能空引用 response.Body[data][0][name].ToString() // 正确写法安全 response.Body[data]?.FirstOrDefault()?[name]?.ToString() // 或更严谨 response.Body[data]?.Any() true ? response.Body[data][0][name]?.ToString() : null工具 UI 中的表达式输入框旁应增加「Safe Navigation Tips」提示告诉用户优先用?操作符。6. 进阶技巧批量请求执行、响应时间监控与 CI/CD 中的静默调用工具的 GUI 界面是为交互设计的但它也预留了命令行接口CLI和自动化扩展点让能力延伸到持续集成场景。这不是「高级功能」而是把日常重复动作固化下来的务实技巧。6.1 批量请求执行用 JSON 配置文件驱动多接口冒烟测试你不需要手动点 20 次发送。工具支持读取一个smoke-test.json配置文件按顺序执行所有请求并汇总结果{ requests: [ { name: Get User List, method: GET, url: https://api.example.com/users, query: { page: 1, size: 10 }, headers: { Authorization: Bearer {{token}} }, assertions: [ { type: status, value: 200 }, { type: schema, file: schemas/user-list.json }, { type: expression, expr: response.Body[\data\].Count() 10 } ] }, { name: Create User, method: POST, url: https://api.example.com/users, headers: { Authorization: Bearer {{token}} }, body: { \name\: \test-{{timestamp}}\, \email\: \testexample.com\ }, content_type: application/json, assertions: [ { type: status, value: 201 }, { type: expression, expr: response.Body[\id\] ! null } ] } ] }执行命令假设编译后主程序为ApiTester.exeApiTester.exe --batch smoke-test.json --output report.html实现原理Program.cs中新增--batch参数解析读取 JSON 后逐个实例化HttpRequestModel调用HttpClientWrapper.SendAsync捕获异常和响应最后用HtmlReportGenerator生成聚合报告。{{token}}和{{timestamp}}是内置变量由工具在运行时替换DateTime.Now.ToString(yyyyMMddHHmmss)。6.2 响应时间监控绘制 P90/P95 延迟曲线识别慢接口单纯看单次响应时间没意义。工具内置「压力模式」对同一请求连续发送 N 次记录每次耗时计算统计值。操作步骤配置好一个请求如 GET/health勾选「Stress Test」复选框设置「Count」为 100「Delay ms」为 100每次间隔 100ms点击发送底部显示Total: 100 | Success: 100 | Avg: 42ms | P90: 68ms | P95: 85ms | Max: 142ms点击「Export Latency Chart」生成latency_20240520.png折线图X轴为请求序号Y轴为耗时ms技术细节StressTestRunner.cs使用Stopwatch精确计时所有请求在单线程中串行执行避免并发干扰数据存入Listlong用OrderBy().Skip(n).First()计算分位数。P90 即第 90 个百分位数times.OrderBy(x x).Skip((int)(times.Count * 0.9)).First()。这比看平均值更能暴露长尾问题。6.3 CI/CD 静默调用在 Azure Pipelines 或 Jenkins 中集成失败时阻断流水线GUI 工具也能进流水线。关键是提供退出码Exit Code成功返回0任意请求失败返回1配置错误返回2。Jenkins Pipeline 示例stage(API Smoke Test) { steps { script { def result sh( script: ApiTester.exe --batch smoke-test.json --output smoke-report.html, returnStatus: true ) if (result ! 0) { echo API 冒烟测试失败退出码 ${result} currentBuild.result FAILURE error API 测试未通过中断构建 } else { echo API 测试通过 archiveArtifacts artifacts: smoke-report.html, fingerprint: true } } } }注意事项在 CI Agent 上需预装对应 .NET Runtime如dotnet-sdk-6.0smoke-test.json中的{{token}}变量应由 Pipeline 注入sh sed -i s/{{token}}/$API_TOKEN/g smoke-test.json报告文件smoke-report.html必须用archiveArtifacts归档供后续人工审查从那以后我每次提交代码前都强制在本地运行一遍ApiTester.exe --batch smoke-test.json再推送到 Git。不是为了炫技而是因为——当某个接口字段悄悄从string改成intSchema 校验会在 0.3 秒内给我一个响亮的耳光。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站