C#如何实现类似Discord.NET命令模式的HttpListener请求处理
实现方案
完全可以实现该需求,核心是自己实现一套特性扫描+路由映射+反射调用的逻辑,和Discord.NET命令处理的底层原理一致,不需要依赖额外的重型框架。
步骤1:定义路由特性
首先定义用于标注方法的HTTP方法特性,示例代码如下:
// 基类HTTP方法特性 [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public abstract class HttpMethodAttribute : Attribute { public string RoutePath { get; } public string HttpMethod { get; } protected HttpMethodAttribute(string httpMethod, string routePath) { HttpMethod = httpMethod; RoutePath = routePath.Trim('/'); } } // POST请求特性 public class HttpPostAttribute : HttpMethodAttribute { public HttpPostAttribute(string routePath) : base("POST", routePath) { } } // GET请求特性,可按相同逻辑扩展PUT、DELETE等 public class HttpGetAttribute : HttpMethodAttribute { public HttpGetAttribute(string routePath) : base("GET", routePath) { } }
步骤2:实现路由注册与请求处理类
该类负责扫描所有带路由特性的方法生成路由表,在HttpListener收到请求时匹配路由并调用对应处理方法:
public class HttpListenerRouter { // 路由映射表:键为`请求方法:路由路径`,值为对应处理方法的元数据 private readonly Dictionary<string, MethodInfo> _routeMap = new(); // 业务处理类的实例 private readonly object _handlerInstance; public HttpListenerRouter(object handlerInstance) { _handlerInstance = handlerInstance; // 构造时扫描所有路由 ScanRoutes(handlerInstance.GetType()); } // 扫描所有带路由特性的方法注册到路由表 private void ScanRoutes(Type handlerType) { var allPublicMethods = handlerType.GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (var method in allPublicMethods) { var attr = method.GetCustomAttribute<HttpMethodAttribute>(); if (attr == null) continue; var routeKey = $"{attr.HttpMethod}:{attr.RoutePath}"; _routeMap[routeKey] = method; } } // 启动HttpListener监听 public async Task StartAsync(string listenerPrefix) { using var listener = new HttpListener(); listener.Prefixes.Add(listenerPrefix); listener.Start(); Console.WriteLine($"监听已启动:{listenerPrefix}"); // 循环接收请求 while (true) { var context = await listener.GetContextAsync(); // 异步处理请求避免阻塞 _ = ProcessRequestAsync(context); } } // 处理单个请求 private async Task ProcessRequestAsync(HttpListenerContext context) { try { var request = context.Request; var requestPath = request.Url.AbsolutePath.Trim('/'); var routeKey = $"{request.HttpMethod}:{requestPath}"; // 路由匹配失败返回404 if (!_routeMap.TryGetValue(routeKey, out var handlerMethod)) { context.Response.StatusCode = 404; context.Response.Close(); return; } // 调用处理方法,可扩展此处实现参数绑定、权限校验等逻辑 handlerMethod.Invoke(_handlerInstance, Array.Empty<object>()); context.Response.StatusCode = 200; context.Response.Close(); } catch (Exception ex) { context.Response.StatusCode = 500; using var writer = new StreamWriter(context.Response.OutputStream); await writer.WriteAsync(ex.Message); context.Response.Close(); } } }
步骤3:业务代码使用特性标注路由
和你期望的写法完全一致,直接在业务方法上标注对应特性即可:
// 业务处理类 public class MyApiHandler { [HttpPost("SomeUrl")] public void SomeMethod() { // 你的业务逻辑 Console.WriteLine("SomeUrl的POST请求已处理"); } [HttpGet("Test/Hello")] public void HelloTest() { Console.WriteLine("Test/Hello的GET请求已处理"); } }
步骤4:启动服务
在程序入口注册业务处理类并启动监听即可:
internal class Program { static async Task Main(string[] args) { var apiHandler = new MyApiHandler(); var router = new HttpListenerRouter(apiHandler); // 监听本地8080端口 await router.StartAsync("http://localhost:8080/"); } }
可扩展能力
如果需要更完善的功能,可以在现有基础上扩展:
- 参数绑定:解析请求的Query、Form、Body内容,和处理方法的参数自动映射
- 中间件逻辑:在路由匹配前后加权限校验、请求日志、异常统一处理等逻辑
- 路由参数支持:支持
/user/{id}这类带占位符的动态路由匹配
内容的提问来源于stack exchange,提问作者IdkWhatToDo
相关产品推荐
相关产品推荐

