using System.Collections.Specialized; using System.Net; using System.Text; namespace XFEExtension.NetCore.CyberComm; /// /// CyberComm HTTP 请求事件参数。新代码应优先使用 。 /// public abstract record CyberCommRequestEventArgs { private readonly HttpListenerRequest? _request; private readonly HttpListenerResponse? _response; protected CyberCommRequestEventArgs(CyberCommHttpRequestContext context) { Context = context; RequestUrl = context.RequestUri; RequestMethod = context.Method; RequestBody = context.Body.IsEmpty ? null : context.RequestBody; RequestHeaders = ToNameValueCollection(context.Headers); QueryString = ToNameValueCollection(context.Query); ClientIP = context.ClientIp; } protected CyberCommRequestEventArgs(Uri? requestUrl, string requestMethod, string? requestBody, NameValueCollection requestHeaders, NameValueCollection queryString, HttpListenerRequest request, HttpListenerResponse response, string clientIP) { RequestUrl = requestUrl; RequestMethod = requestMethod; RequestBody = requestBody; RequestHeaders = requestHeaders; QueryString = queryString; _request = request; _response = response; ClientIP = clientIP; } public CyberCommHttpRequestContext? Context { get; } public Uri? RequestUrl { get; } public string RequestMethod { get; } public string? RequestBody { get; } public NameValueCollection RequestHeaders { get; } public NameValueCollection QueryString { get; } /// 旧 HttpListener 请求。Socket 传输模式下为 null。 [Obsolete("请使用 Context 和与监听器无关的请求属性")] public HttpListenerRequest? Request => _request; /// 旧 HttpListener 响应。Socket 传输模式下为 null。 [Obsolete("请使用 Context.Response 或 ReplyAndClose")] public HttpListenerResponse? Response => _response; public string ClientIP { get; } /// 实际接受当前连接的本地监听端口;旧 HttpListener 兼容模式下取请求 URL 端口。 public int LocalPort => Context?.LocalPort ?? RequestUrl?.Port ?? 0; /// 贯穿请求、响应和日志的关联标识。 public string CorrelationId => Context?.CorrelationId ?? string.Empty; /// /// 从跨平台 HTTP 上下文创建兼容事件参数。 /// public static CyberCommRequestEventArgs FromContext(CyberCommHttpRequestContext context) => new CyberCommRequestEventArgsImpl(context); public async Task ReplyAndClose(string message, HttpStatusCode statusCode = HttpStatusCode.OK) => await ReplyAndClose(message, statusCode, "text/plain; charset=utf-8").ConfigureAwait(false); /// 写入带内容类型的响应并结束请求。 public async Task ReplyAndClose(string message, HttpStatusCode statusCode, string contentType) { if (Context is not null) { await Context.Response.WriteTextAsync(message, statusCode, contentType).ConfigureAwait(false); Context.Response.Complete(); return; } var response = _response ?? throw new InvalidOperationException("当前请求没有响应对象"); response.StatusCode = (int)statusCode; response.ContentType = contentType; var buffer = Encoding.UTF8.GetBytes(message); response.ContentLength64 = buffer.Length; await response.OutputStream.WriteAsync(buffer).ConfigureAwait(false); response.Close(); } public async Task ReplyMessage(string message) { if (Context is not null) { await Context.Response.WriteTextAsync(message).ConfigureAwait(false); return; } var response = _response ?? throw new InvalidOperationException("当前请求没有响应对象"); var buffer = Encoding.UTF8.GetBytes(message); response.ContentLength64 = buffer.Length; await response.OutputStream.WriteAsync(buffer).ConfigureAwait(false); } public async Task ReplyBinaryMessage(byte[] bytes) { if (Context is not null) { await Context.Response.WriteAsync(bytes).ConfigureAwait(false); return; } var response = _response ?? throw new InvalidOperationException("当前请求没有响应对象"); response.ContentLength64 = bytes.Length; await response.OutputStream.WriteAsync(bytes).ConfigureAwait(false); } public void Close(HttpStatusCode statusCode = HttpStatusCode.OK) { if (Context is not null) { Context.Response.Complete(statusCode); return; } var response = _response ?? throw new InvalidOperationException("当前请求没有响应对象"); response.StatusCode = (int)statusCode; response.Close(); } public void CloseWhitBadRequest() => Close(HttpStatusCode.BadRequest); private static NameValueCollection ToNameValueCollection(IReadOnlyDictionary> source) { var result = new NameValueCollection(StringComparer.OrdinalIgnoreCase); foreach (var (name, values) in source) foreach (var value in values) result.Add(name, value); return result; } }