C/S客户端程序 winform接收外部http (GET|POST)请求 工具类逻辑开发
前言
我们知道web项目(即B/S端程序的S端)是很容易提供API接口,供外部进行访问的,这是Web本身的特性所然。Web项目在发布后,会挂载到比如IIS管理器,上面会要求配置IP和端口号,外部访问时根据约定的IP,端口,以及约定的路由路径、请求方式、传参等就很容易外部对内API接口访问。
客户端程序(即C/S端程序的C端)对外提供API接口则不那么容易,毕竟本身客户端没有明确需要表面自身身份的IP和端口,发布好的C/S端程序拷到哪台机器都可以使用!
问题
现在有特殊场景需求,我们开发的winform (C/S架构)程序需要和上层的总控系统对接,而总控系统可能是B/S架构,它需要和我的winform程序进行接口对接,需要我暴露自身的接口,由它来调用。
这就面临一个问题:winform如何能接收外部http (GET|POST)请求?
解决过程
1.我们现在原有的项目中,新建一个HttpServerHelper类,内部封装一个Http服务器逻辑,将winform程序当作一个Http服务器,实时监听服务器的端口有没有被访问,一旦被访问则执行相关逻辑

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace DetectMonitor.Helper
{
public class HttpServerHelper
{
private readonly string selfUrl;//客户端自身(也就是该程序本身)作为服务器,需要确认以自身哪个端口来对外监听外部的访问
private readonly HttpListener myListener;
private readonly Dictionary<string, Func<HttpListenerContext, Task<string>>> myRoutes; //路由映射
//构造函数 //selfListenAddrs:实际给一个 http://ip:port/就行,不用搞太多端口区分
public HttpServerHelper(string[] selfListenAddrs)
{
if (!HttpListener.IsSupported)
{
throw new NotSupportedException("当前平台不支持HttpListener");
}
myListener = new HttpListener();//初始化HttpListener实例
//初始化路由映射字典
myRoutes = new Dictionary<string, Func<HttpListenerContext, Task<string>>>();//
//为服务器(就是自身)添加地址和端口 (实际)
foreach (string addr in selfListenAddrs)
{
myListener.Prefixes.Add(addr); //内容格式http://ip:port/api/
}
selfUrl = selfListenAddrs[0];//记录第一个监听的地址
}
//判断监听实例是否在监听
public bool IsOpen
{
get { return myListener.IsListening; }
}
//启动服务器
public void Start()
{
myListener.Start();
myListener.BeginGetContext(ProcessRequestCallback,myListener);//处理客户端请求
}
//停止服务器
public void Stop()
{
myListener.Stop();
myListener.Close();
}
//添加路由和处理程序的映射关系
public void AddRoute(string route, Func<HttpListenerContext,Task<string>> handler)
{
myRoutes.Add(route, handler);
}
//处理客户端(即外部程序)发来请求的处理逻辑 (这里是定义的回调函数)
private async void ProcessRequestCallback(IAsyncResult result)
{
HttpListener listener = (HttpListener)result.AsyncState;
//开始下一个请求的监听
listener.BeginGetContext(ProcessRequestCallback,listener);
try
{
HttpListenerContext context = listener.EndGetContext(result);
//获取请求方法和URL路径
string httpMethod = context.Request.HttpMethod;
string url = context.Request.Url.AbsolutePath;
string responseString = "No Data!";//默认响应字符串
Func<HttpListenerContext, Task<string>> handler;
//如果请求路径存在与路由映射中,执行相应的处理程序
if (myRoutes.TryGetValue(url, out handler))
{
//获取处理程序返回的响应数据
responseString = await handler(context);
//将响应数据编码成字节数组
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
//设置响应的内容长度和状态码
context.Response.ContentLength64 = buffer.Length;
context.Response.StatusCode = (int)HttpStatusCode.OK;
// 设置响应头中的 Content-Type 为 application/json 并指定字符编码为 UTF-8(修复浏览器访问GET请求时,反馈值中文乱码问题)
context.Response.ContentType = "application/json; charset=utf-8";
//将响应写入输出流并关闭输出流
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
context.Response.OutputStream.Close();
}
else
{
//如果请求不存在与路由映射中,返回404错误
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
context.Response.Close();
}
}catch (Exception ex)
{
Log4NetHelper.Error("HttpServerHelper.ProcessRequestCallback:" + ex.Message);
}
}
}
}
2. 编写HttpServer的监听并调用对应接口的逻辑,在项目的加载函数或初始化函数调用执行,这样实时监听外部的端口访问,一旦监听到,就分析访问的路由,调用对应路由映射的函数
//20250102 本机C/S客户端程序提供开放接口需求,供外部调用
public static void IniHttpServer()
{
//注意:参数内的IP就是本机以后对外身份的ip(正式使用需要修改), 参数内的端口就是本机对外接受“接口”访问的端口
HttpServerHelper httpServer = new HttpServerHelper(new string[]
{
"http://127.0.0.1:7777/"
});
//绑定映射,处理函数 (路径就是将来给到外部来访问的路径,函数就是路径(接口)被访问后执行自己自定义相关逻辑函数)
//(1)如果外部访问模式3,路径为:http://127.0.0.1:7777/pattern_three,那么外部访问该路径后,会调用DetectObjectDeal函数处理函数
httpServer.AddRoute("/api/detectobject", DetectObjectDeal);
//(2)同理,如果外部访问模式2:根据外部系统传入的指定测试区域,执行区域内若干摄像头按指定间隔时间自动循环切换
httpServer.AddRoute("/api/testarea", TestAreaDeal);
//(3)同理,如果外部访问模式1:根据外部系统传入的指定摄像头,执行指定摄像头的切换
httpServer.AddRoute("/api/appointcamera", AppointCameraDeal);
httpServer.Start();
}
//模式3:根据外部系统传入的指定监测对象,执行yolo目标监测
public static async Task<string> DetectObjectDeal(HttpListenerContext context)
{
string httpMethod = context.Request.HttpMethod; //获取外部访问接口使用的是什么方法(理应事先应约定:GET POST)
Log4NetHelper.Info("接口模拟测试-----请求方法判别:" + httpMethod);
string responseString = "这是我初始给的内容";//接口被调用返回内容
//判别为GET请求
if (httpMethod.Equals("GET", StringComparison.OrdinalIgnoreCase))
{
// 处理 GET 请求
//string queryString = context.Request.QueryString.ToString();
NameValueCollection queryString = context.Request.QueryString;
if (queryString.HasKeys())
{
// 处理路径上直接传参的情况
//后面要按照约定的请求传参格式,进行对应的解析。。。
Log4NetHelper.Info("接口模拟测试-----GET请求带参数, 参数全部Keys内容:" + string.Join(", ", queryString.AllKeys));
//假设模拟的GET请求参数 http://127.0.0.1:7777/detectobject?myname="chaochao"& myage=18 进行解析
//var queryParams = System.Web.HttpUtility.ParseQueryString();
//string paramValue1 = queryParams["myname"];
// string paramValue2 = queryParams["myage"];
//Log4NetHelper.Info("接口模拟测试-----GET请求带参数,参数解析:myname:" + paramValue1 + ";myage: " + paramValue2);
// 构建日志信息,显示所有键值对
string logInfo = "接口模拟测试-----GET请求带参数, 具体键值内容:";
foreach (string key in queryString.AllKeys)
{
logInfo += key + ": " + queryString[key] + "; ";
}
Log4NetHelper.Info(logInfo);
// 解析查询字符串并执行相应的逻辑
string paramValue1 = queryString["myname"];
int paramValue2 = Convert.ToInt32(queryString["myage"]);
Log4NetHelper.Info("接口模拟测试-----GET请求带参数, 参数解析:myname: " + paramValue1 + "; myage: " + paramValue2);
// 处理带参数的 GET 请求
responseString = "{\"code\":\"200\",\"message\":\"GET请求带参数处理成功\", \"myname\":\"" + paramValue1 + "\", \"myage\":\"" + paramValue2 + "\"}";
}
else
{
// 处理路径上不传参的情况
Log4NetHelper.Info("接口模拟测试-----GET请求不带参数");
responseString = "{\"code\":\"200\",\"message\":\"GET请求不带参数处理成功\"}";
//可能直接处理相关业务逻辑。。。
}
}
//判别为POST请求
else if (httpMethod.Equals("POST", StringComparison.OrdinalIgnoreCase))
{
// 处理 POST 请求
if (context.Request.HasEntityBody)
{
using (Stream body = context.Request.InputStream)
{
using (StreamReader reader = new StreamReader(body, context.Request.ContentEncoding))
{
string postData = await reader.ReadToEndAsync(); // 读取 POST 数据
//处理数据
//。。。。。
//返回数据(约定返回给外部请求方)
responseString = "{\"code\":\"200\",\"message\":\"处理成功\"}";//后面根据实际约定进行定义
Log4NetHelper.Info("接口模拟测试-----POST请求数据:" + postData);
}
}
}
}
return responseString;
}
3.运行winform程序,通过postman测试POST请求

4.运行winform程序,通过浏览器或postman测试GET请求


浙公网安备 33010602011771号