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服务器,实时监听服务器的端口有没有被访问,一旦被访问则执行相关逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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的监听并调用对应接口的逻辑,在项目的加载函数或初始化函数调用执行,这样实时监听外部的端口访问,一旦监听到,就分析访问的路由,调用对应路由映射的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//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请求

posted @   上清风  阅读(216)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示