You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

189 lines
6.4 KiB

4 years ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Net;
  5. namespace Apewer.Web
  6. {
  7. /// <summary>用于网站的服务程序。</summary>
  8. public class HttpListenerProvider : ApiProvider
  9. {
  10. private HttpListenerContext context;
  11. private HttpListenerRequest request;
  12. private HttpListenerResponse response;
  13. /// <summary>创建服务程序实例。</summary>
  14. /// <exception cref="ArgumentNullException"></exception>
  15. public HttpListenerProvider(HttpListenerContext context)
  16. {
  17. if (context == null) throw new ArgumentNullException(nameof(context));
  18. this.context = context;
  19. request = context.Request;
  20. response = context.Response;
  21. }
  22. #region Implement
  23. /// <summary>调用前的检查,返回错误信息。</summary>
  24. public override string PreInvoke() { return null; }
  25. /// <summary>读取请求前的检查,返回错误信息。</summary>
  26. public override string PreRead() { return null; }
  27. /// <summary>写入响应前的检查,返回错误信息。</summary>
  28. public override string PreWrite()
  29. {
  30. if (response == null) return null;
  31. // 从 Response 头中移除 Server 和 X-Powered-By 属性。
  32. var keys = new List<string>(response.Headers.AllKeys);
  33. if (keys.Contains("Server")) response.Headers.Remove("Server");
  34. if (keys.Contains("X-Powered-By")) response.Headers.Remove("X-Powered-By");
  35. return null;
  36. }
  37. /// <summary>向发送缓冲区的数据,之后停止并关闭响应流。</summary>
  38. public override void End() => End(true);
  39. /// <summary>释放非托管资源。</summary>
  40. public override void Dispose() { }
  41. #endregion
  42. #region Request
  43. /// <summary>获取 HTTP 方法。</summary>
  44. public override Network.HttpMethod GetMethod() => ApiUtility.Method(request.HttpMethod);
  45. /// <summary>获取客户端的 IP 地址。</summary>
  46. public override string GetClientIP() => request.UserHostAddress;
  47. /// <summary>获取请求的 URL。</summary>
  48. public override Uri GetUrl() => request.Url;
  49. /// <summary>获取请求的 Referrer。</summary>
  50. public override string GetReferrer() => request.UrlReferrer == null ? null : request.UrlReferrer.OriginalString;
  51. /// <summary>获取请求的头。</summary>
  52. public override StringPairs GetHeaders() => StringPairs.From(request.Headers);
  53. /// <summary>获取请求的内容类型。</summary>
  54. public override string GetContentType() => request.ContentType;
  55. /// <summary>获取请求的内容长度。</summary>
  56. public override long GetContentLength() => request.ContentLength64;
  57. /// <summary>获取请求的内容。</summary>
  58. public override Stream RequestBody() => request.InputStream;
  59. #endregion
  60. #region Response
  61. /// <summary>设置 HTTP 状态。</summary>
  62. public override void SetStatus(string status) => response.StatusCode = Convert.ToInt32(status);
  63. /// <summary>设置响应的头。</summary>
  64. public override string SetHeader(string name, string value)
  65. {
  66. if (response == null || string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) return "参数无效。";
  67. try
  68. {
  69. response.AddHeader(name, value);
  70. return null;
  71. }
  72. catch (Exception ex) { return ex.Message; }
  73. }
  74. /// <summary>设置响应的缓存秒数,设置为 0 即不允许缓存。</summary>
  75. public override void SetCache(int seconds)
  76. {
  77. if (seconds > 0)
  78. {
  79. SetHeader("Cache-Control", $"public, max-age={seconds}, s-maxage={seconds}");
  80. return;
  81. }
  82. SetHeader("Cache-Control", "no-cache, no-store, must-revalidate");
  83. SetHeader("Pragma", "no-cache");
  84. }
  85. /// <summary>设置响应的缓存秒数。</summary>
  86. public override void SetContentType(string value) => response.ContentType = value;
  87. /// <summary>设置响应的内容长度。</summary>
  88. public override void SetContentLength(long value) => response.ContentLength64 = value;
  89. /// <summary>设置响应重定向。</summary>
  90. /// <remarks>响应 302 状态。</remarks>
  91. public override void SetRedirect(string location)
  92. {
  93. try { response.Redirect(location); } catch { }
  94. }
  95. /// <summary>获取 Response 流。</summary>
  96. public override Stream ResponseBody() => response.OutputStream;
  97. #endregion
  98. #region This
  99. /// <summary>停止并关闭响应流。可指定向发送缓冲区的数据。</summary>
  100. public void End(bool flush)
  101. {
  102. if (response == null) return;
  103. try { if (flush) response.OutputStream.Flush(); } catch { }
  104. try { response.Close(); } catch { }
  105. }
  106. /// <summary>获取 Cookies。</summary>
  107. public StringPairs GetCookies()
  108. {
  109. var sp = new StringPairs();
  110. if (request != null && request.Cookies != null)
  111. {
  112. for (var i = 0; i < request.Cookies.Count; i++)
  113. {
  114. try
  115. {
  116. var cookie = request.Cookies[i];
  117. var key = cookie.Name;
  118. var value = cookie.Value ?? "";
  119. sp.Add(new KeyValuePair<string, string>(key, value));
  120. }
  121. catch { }
  122. }
  123. }
  124. return sp;
  125. }
  126. /// <summary>设置响应的 Cookie。</summary>
  127. public string SetCookie(string key, string value)
  128. {
  129. if (response == null) return null;
  130. var k = key.ToTrim();
  131. var v = value.ToTrim();
  132. if (k.IsEmpty()) return "参数 Key 无效。";
  133. try
  134. {
  135. var cookie = new Cookie(k, v);
  136. var now = DateTime.Now;
  137. cookie.Expires = v.IsEmpty() ? now.AddDays(-1) : now.AddYears(1);
  138. response.SetCookie(cookie);
  139. return null;
  140. }
  141. catch (Exception ex)
  142. {
  143. return ex.Message;
  144. }
  145. }
  146. #endregion
  147. }
  148. }