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.

856 lines
31 KiB

  1. using Apewer;
  2. using Apewer.Models;
  3. using Apewer.Network;
  4. using Apewer.Source;
  5. using System;
  6. using System.Collections;
  7. using System.Collections.Generic;
  8. using System.Diagnostics;
  9. using System.IO;
  10. using System.Net;
  11. using System.Reflection;
  12. using System.Text;
  13. #if NETFX
  14. using System.Web;
  15. #endif
  16. #if NETCORE
  17. using Microsoft.AspNetCore.Http;
  18. #endif
  19. namespace Apewer.Web
  20. {
  21. internal abstract class ApiProcessor
  22. {
  23. // 请求 Body 大于 100MB 时,不尝试解析 API。
  24. const long MaxRequestBody = 100L * 1024L * 1024L;
  25. #region fields & properties
  26. internal ApiInvoker Invoker = null;
  27. private DateTime Beginning = DateTime.Now;
  28. private DateTime Ending;
  29. private ApiRequest ApiRequest;
  30. private ApiResponse ApiResponse;
  31. public Dictionary<string, ApiApplication> Entries;
  32. public bool HaveContext { get; protected set; }
  33. // 抽象方法的返回值。
  34. private Uri Url;
  35. private HttpMethod Method;
  36. protected abstract object GetContext();
  37. protected abstract string GetClientIP();
  38. protected abstract HttpMethod GetMethod();
  39. protected abstract Uri GetURL();
  40. protected abstract Uri GetReferrer();
  41. protected abstract StringPairs GetHeaders();
  42. protected abstract StringPairs GetCookies();
  43. protected abstract long GetRequestLength();
  44. protected abstract byte[] ReadRequest();
  45. protected abstract void SetCacheControl(int seconds);
  46. protected abstract void SetContentType(string value);
  47. protected abstract void SetContentLength(long value);
  48. protected abstract void Redirect(string url);
  49. protected abstract string AddHeader(string name, string value);
  50. protected abstract void WriteResponse(byte[] bytes);
  51. protected abstract void WriteResponse(Stream stream);
  52. #endregion
  53. /// <summary>执行处理程序,返回错误信息。</summary>
  54. public string Run()
  55. {
  56. try
  57. {
  58. // 检查依赖的属性,若不通过,则无法执行。
  59. var preCheckError = PreCheck();
  60. if (!string.IsNullOrEmpty(preCheckError)) return preCheckError;
  61. // 准备变量。
  62. ApiRequest = GetRequest();
  63. ApiResponse = new ApiResponse();
  64. // 准备向 Controller 传递的属性。
  65. var an = ApiRequest.Application;
  66. var a = null as ApiApplication;
  67. var fn = ApiRequest.Function;
  68. var f = null as ApiFunction;
  69. var r = ApiRequest.Random;
  70. var t = ApiRequest.Ticket;
  71. // 调用。
  72. if (Entries.ContainsKey(an))
  73. {
  74. a = Entries[an];
  75. if (a.Functions.ContainsKey(fn))
  76. {
  77. f = a.Functions[fn];
  78. Invoke(a, f, ApiRequest, ApiResponse);
  79. }
  80. else
  81. {
  82. Invoke(a, null, ApiRequest, ApiResponse);
  83. }
  84. }
  85. else
  86. {
  87. var @default = ApiOptions.Default;
  88. if (@default != null)
  89. {
  90. Invoke(ApiInvoker.GetApplication(@default, false), null, ApiRequest, ApiResponse);
  91. }
  92. else
  93. {
  94. ApiResponse.Error("指定的 Application 无效。");
  95. if (an.IsEmpty() && ApiOptions.AllowEnumerate) Enumerate(Entries);
  96. }
  97. }
  98. // 记录结束时间。
  99. Ending = DateTime.Now;
  100. // 调整响应。
  101. ApiResponse.Beginning = Beginning;
  102. ApiResponse.Ending = Ending;
  103. ApiResponse.Application = an;
  104. ApiResponse.Function = fn;
  105. ApiResponse.Random = r;
  106. // 向客户端输出。
  107. return Output(ApiResponse);
  108. }
  109. catch (Exception ex)
  110. {
  111. Logger.Web.Exception(this, ex);
  112. return ex.Message;
  113. }
  114. }
  115. // 检查执行的前提条件。
  116. string PreCheck()
  117. {
  118. // Context
  119. if (!HaveContext) return "Context 无效。";
  120. // Entries
  121. if (Entries == null) return "Entries 无效。";
  122. // Method
  123. Method = GetMethod();
  124. if (Method == HttpMethod.NULL) return "Methods 无效。";
  125. // AccessControl
  126. // 在此之后可以输出
  127. if (ApiOptions.WithAccessControl)
  128. {
  129. AddHeader("Access-Control-Allow-Headers", "Content-Type");
  130. AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
  131. AddHeader("Access-Control-Allow-Origin", "*");
  132. var maxage = ApiOptions.AccessControlMaxAge;
  133. if (maxage > 0) AddHeader("Access-Control-Max-Age", maxage.ToString());
  134. }
  135. // URL
  136. Url = GetURL();
  137. var lowerPath = TextUtility.ToLower(Url.AbsolutePath);
  138. // favicon.ico
  139. if (!ApiOptions.AllowFavIcon)
  140. {
  141. if (lowerPath.StartsWith("/favicon.ico"))
  142. {
  143. SetCacheControl(0);
  144. Output(null as byte[]);
  145. return "已取消对 favicon.ico 的请求。";
  146. }
  147. }
  148. // robots.txt
  149. if (!ApiOptions.AllowRobots)
  150. {
  151. if (lowerPath.StartsWith("/robots.txt"))
  152. {
  153. const string text = "User-agent: *\nDisallow: / \n";
  154. SetCacheControl(0);
  155. Output(text);
  156. return "已取消对 robots.txt 的请求。";
  157. }
  158. }
  159. return null;
  160. }
  161. void Enumerate(Dictionary<string, ApiApplication> applications)
  162. {
  163. var count = 0;
  164. var list = Json.NewArray();
  165. if (applications != null)
  166. {
  167. foreach (var i in applications)
  168. {
  169. if (!i.Value.Visible) continue;
  170. var item = Json.NewObject();
  171. item["name"] = i.Value.Name;
  172. item["caption"] = i.Value.Caption;
  173. if (ApiOptions.WithModuleName) item["module"] = i.Value.Module;
  174. if (ApiOptions.WithTypeName) item["type"] = i.Value.Type.FullName;
  175. list.AddItem(item);
  176. count = count + 1;
  177. }
  178. }
  179. ApiResponse.Data.Reset(Json.NewObject());
  180. ApiResponse.Data.SetProperty("count", count);
  181. ApiResponse.Data.SetProperty("list", list);
  182. }
  183. void Enumerate(Dictionary<string, ApiFunction> functions)
  184. {
  185. var count = 0;
  186. var list = Json.NewArray();
  187. if (functions != null)
  188. {
  189. foreach (var i in functions)
  190. {
  191. if (!i.Value.Visible) continue;
  192. var item = Json.NewObject();
  193. item["name"] = i.Value.Name;
  194. item["caption"] = i.Value.Caption;
  195. item["description"] = i.Value.Description;
  196. list.AddItem(item);
  197. count = count + 1;
  198. }
  199. }
  200. ApiResponse.Data.Reset(Json.NewObject());
  201. ApiResponse.Data.SetProperty("count", count);
  202. ApiResponse.Data.SetProperty("list", list);
  203. }
  204. internal static ApiController CreateController(Type type)
  205. {
  206. try
  207. {
  208. return (ApiController)Activator.CreateInstance(type);
  209. }
  210. catch { }
  211. return null;
  212. }
  213. void Invoke(ApiApplication application, ApiFunction function, ApiRequest request, ApiResponse response)
  214. {
  215. if (request == null || response == null) return;
  216. // 创建控制器实例。
  217. if (application == null)
  218. {
  219. response.Error("指定的 Application 无效,无法创建实例。");
  220. return;
  221. }
  222. var controller = CreateController(application.Type);
  223. if (controller == null)
  224. {
  225. response.Error("创建控制器实例失败。");
  226. return;
  227. }
  228. controller.Application = application;
  229. controller.Function = function;
  230. controller.Request = request;
  231. controller.Response = response;
  232. Invoke(controller);
  233. }
  234. void Invoke(ApiController controller)
  235. {
  236. if (controller == null) return;
  237. var application = controller.Application;
  238. var function = controller.Function;
  239. var request = controller.Request;
  240. var response = controller.Response;
  241. if (application == null) return;
  242. if (request == null || response == null) return;
  243. // 调用功能。
  244. try
  245. {
  246. if (controller.AfterInitialized != null) controller.AfterInitialized.Invoke();
  247. // 控制器要求单独处理,此时框架不再处理 Function。
  248. var allowFunction = controller.AllowFunction;
  249. if (application.Independent) allowFunction = false;
  250. // 由框架调用 Function。
  251. if (allowFunction)
  252. {
  253. if (function == null)
  254. {
  255. var @default = controller.DefaultFunction;
  256. if (@default != null)
  257. {
  258. @default.Invoke();
  259. }
  260. else
  261. {
  262. controller.Response.Error("指定的 Function 无效。");
  263. if (ApiOptions.AllowEnumerate) Enumerate(application.Functions);
  264. }
  265. }
  266. else
  267. {
  268. var result = function.Method.Invoke(controller, null);
  269. if (response.Status.IsBlank()) response.Status = "ok";
  270. if (function.Returnable && result != null)
  271. {
  272. if (result is string)
  273. {
  274. var error = (string)result;
  275. if (!string.IsNullOrEmpty(error)) response.Error(error);
  276. }
  277. }
  278. }
  279. }
  280. }
  281. catch (Exception exception)
  282. {
  283. response.Error(exception.InnerException);
  284. }
  285. // 释放控制器。
  286. RuntimeUtility.Dispose(controller);
  287. }
  288. void Output(string text, string type = "text/plain; charset=utf-8")
  289. {
  290. var bytes = TextUtility.ToBinary(text);
  291. Output(bytes, type);
  292. }
  293. void Output(byte[] bytes, string type = "application/octet-stream")
  294. {
  295. var length = bytes == null ? 0 : bytes.LongLength;
  296. SetContentType(type);
  297. SetContentLength(length);
  298. if (bytes != null && bytes.LongLength > 0L)
  299. {
  300. WriteResponse(bytes);
  301. }
  302. }
  303. void Output(Stream stream, string type = "application/octet-stream")
  304. {
  305. SetContentType(type);
  306. if (stream != null)
  307. {
  308. var length = stream.Length - stream.Position;
  309. SetContentLength(length);
  310. if (length > 0 && stream.CanRead) WriteResponse(stream);
  311. RuntimeUtility.Dispose(stream);
  312. }
  313. }
  314. // 输出 ApiResponse,返回错误信息。
  315. string Output(ApiResponse apiResponse)
  316. {
  317. // Ticket。
  318. if (ApiRequest.Method == HttpMethod.GET)
  319. {
  320. if (apiResponse.Ticket != null && !apiResponse.Cookies.HasKey("ticket", true))
  321. {
  322. apiResponse.Cookies.Add("ticket", apiResponse.Ticket);
  323. }
  324. }
  325. // 设置自定义头。
  326. AddHeaders(apiResponse.Headers);
  327. var setCookies = SetCookies(apiResponse.Cookies);
  328. if (setCookies.NotEmpty())
  329. {
  330. apiResponse.Error("系统错误。");
  331. apiResponse.Data.Reset(Json.NewObject());
  332. apiResponse.Data["cookies"] = setCookies;
  333. }
  334. //
  335. if (apiResponse.Type == ApiFormat.Redirect)
  336. {
  337. SetCacheControl(0);
  338. Redirect(apiResponse.RedirectUrl);
  339. return null;
  340. }
  341. //
  342. if (apiResponse.Type == ApiFormat.Json)
  343. {
  344. SetCacheControl(apiResponse.Expires);
  345. Output(ExportJson(apiResponse));
  346. return null;
  347. }
  348. //
  349. if (apiResponse.Type == ApiFormat.Text)
  350. {
  351. SetCacheControl(apiResponse.Expires);
  352. Output(apiResponse.TextString, apiResponse.TextType);
  353. return null;
  354. }
  355. //
  356. if (apiResponse.Type == ApiFormat.Binary)
  357. {
  358. var type = apiResponse.BinaryType ?? "application/octet-stream";
  359. if (apiResponse.BinaryBytes != null)
  360. {
  361. SetCacheControl(apiResponse.Expires);
  362. Output(apiResponse.BinaryBytes, type);
  363. }
  364. else if (apiResponse.BinaryStream != null)
  365. {
  366. SetCacheControl(apiResponse.Expires);
  367. Output(apiResponse.BinaryStream, type);
  368. }
  369. return null;
  370. }
  371. //
  372. if (apiResponse.Type == ApiFormat.File)
  373. {
  374. if (apiResponse.FileStream != null)
  375. {
  376. SetCacheControl(apiResponse.Expires);
  377. AddHeader("Content-Disposition", $"attachment; filename={TextUtility.EncodeUrl(apiResponse.FileName)}");
  378. Output(apiResponse.FileStream, apiResponse.FileType ?? "application/octet-stream");
  379. }
  380. return null;
  381. }
  382. // Context.Response.Flush();
  383. // Context.Response.End();
  384. return null;
  385. }
  386. void AddHeaders(StringPairs headers)
  387. {
  388. foreach (var header in headers) AddHeader(header.Key, header.Value);
  389. }
  390. string SetCookies(IEnumerable<KeyValuePair<string, string>> cookies)
  391. {
  392. if (cookies == null) return null;
  393. var ps = new List<string>();
  394. foreach (var kvp in cookies)
  395. {
  396. var k = kvp.Key ?? "";
  397. var v = kvp.Value ?? "";
  398. if (k.IsEmpty()) continue;
  399. ps.Add(TextUtility.Merge(k, "=", v));
  400. }
  401. if (ps.Count < 1) return null;
  402. var value = TextUtility.Join("; ", ps);
  403. return AddHeader("Set-Cookie", value);
  404. }
  405. // 从 Context 获取 ApiRequest 模型。
  406. ApiRequest GetRequest()
  407. {
  408. // 检查 URL 的 Path。
  409. // if (string.IsNullOrEmpty(request.Path)) return null;
  410. // 创建数据对象。
  411. var apiRequest = new ApiRequest();
  412. // Http Method。
  413. apiRequest.Method = Method;
  414. // 基本信息。
  415. apiRequest.Headers = GetHeaders();
  416. apiRequest.IP = GetClientIP();
  417. apiRequest.Url = Url;
  418. apiRequest.Referrer = GetReferrer();
  419. apiRequest.Parameters = WebUtility.ParseParameters(Url.Query);
  420. // Headers。
  421. apiRequest.UserAgent = WebUtility.GetUserAgent(apiRequest.Headers);
  422. apiRequest.Cookies = GetCookies();
  423. // 匹配 API。
  424. var application = null as string;
  425. var function = null as string;
  426. var random = null as string;
  427. var ticket = null as string;
  428. var session = null as string;
  429. var page = null as string;
  430. // 解析 POST 请求。
  431. if (apiRequest.Method == Apewer.Network.HttpMethod.POST)
  432. {
  433. var post = null as byte[];
  434. var length = GetRequestLength();
  435. if (length < 0 || (length > 0 && length <= MaxRequestBody))
  436. {
  437. post = ReadRequest();
  438. }
  439. // 解析 POST。
  440. if (post != null && post.Length > 1L)
  441. {
  442. // 检查 JSON 的格式,第一位必须是“{”或“[”。
  443. if (post[0] == 123 || post[0] == 91)
  444. {
  445. var text = TextUtility.FromBinary(post);
  446. var json = Json.Parse(text);
  447. if (json != null && json.IsObject)
  448. {
  449. application = json["application"];
  450. function = json["function"];
  451. random = json["random"];
  452. ticket = json["ticket"];
  453. session = json["session"];
  454. page = json["page"];
  455. var data = json.GetProperty("data");
  456. apiRequest.PostData = post;
  457. apiRequest.PostText = text;
  458. apiRequest.PostJson = json;
  459. apiRequest.Data = data ?? Json.NewObject();
  460. }
  461. }
  462. }
  463. }
  464. // 解析 URL 参数。
  465. // URL 参数的优先级应高于 URL 路径,以避免反向代理产生的路径问题。
  466. if (string.IsNullOrEmpty(application)) application = WebUtility.GetParameter(apiRequest.Parameters, "application");
  467. if (string.IsNullOrEmpty(function)) function = WebUtility.GetParameter(apiRequest.Parameters, "function");
  468. if (string.IsNullOrEmpty(random)) random = WebUtility.GetParameter(apiRequest.Parameters, "random");
  469. if (string.IsNullOrEmpty(ticket)) ticket = WebUtility.GetParameter(apiRequest.Parameters, "ticket");
  470. if (string.IsNullOrEmpty(session)) session = WebUtility.GetParameter(apiRequest.Parameters, "session");
  471. if (string.IsNullOrEmpty(page)) page = WebUtility.GetParameter(apiRequest.Parameters, "page");
  472. // 从 Cookie 中获取 Ticket。
  473. if (string.IsNullOrEmpty(ticket)) ticket = apiRequest.Cookies.GetValue("ticket");
  474. // 最后检查 URL 路径。
  475. var paths = (apiRequest.Url.AbsolutePath ?? "").Split('/');
  476. if (string.IsNullOrEmpty(application) && paths.Length >= 2) application = TextUtility.DecodeUrl(paths[1]);
  477. if (string.IsNullOrEmpty(function) && paths.Length >= 3) function = TextUtility.DecodeUrl(paths[2]);
  478. // 修正内容。
  479. application = application.SafeLower().SafeTrim();
  480. function = function.SafeLower().SafeTrim();
  481. random = random.SafeLower().SafeTrim();
  482. ticket = ticket.SafeLower().SafeTrim();
  483. session = session.SafeLower().SafeTrim();
  484. page = page.SafeTrim();
  485. // 设置请求:回传。
  486. apiRequest.Application = application;
  487. apiRequest.Function = function;
  488. apiRequest.Random = random;
  489. // 设置请求:不回传。
  490. apiRequest.Ticket = ticket;
  491. apiRequest.Session = session;
  492. apiRequest.Page = page;
  493. // 返回结果。
  494. return apiRequest;
  495. }
  496. private static string ExportJson(ApiResponse response)
  497. {
  498. if (response == null) return "{}";
  499. var json = Json.NewObject();
  500. // 执行时间。
  501. if (ApiOptions.WithClock)
  502. {
  503. json.SetProperty("clock", response.Ending.ToLucid());
  504. }
  505. // 持续时间。
  506. if (ApiOptions.WithDuration)
  507. {
  508. var seconds = Math.Floor((response.Ending - response.Beginning).TotalMilliseconds) / 1000D;
  509. json.SetProperty("duration", seconds);
  510. }
  511. // 随机值。
  512. if (response.Random.NotEmpty()) json.SetProperty("random", response.Random);
  513. // 调用。
  514. if (ApiOptions.WithTarget)
  515. {
  516. json.SetProperty("application", response.Application);
  517. json.SetProperty("function", response.Function);
  518. }
  519. // 状态。
  520. json.SetProperty("status", (TextUtility.IsBlank(response.Status) ? TextUtility.EmptyString : response.Status.ToLower()));
  521. json.SetProperty("message", response.Message);
  522. // Ticket。
  523. if (response.Ticket != null) json.SetProperty("ticket", response.Ticket);
  524. // 异常。
  525. if (ApiOptions.AllowException && response.Exception != null)
  526. {
  527. try
  528. {
  529. var exMessage = null as string;
  530. var exStackTrace = null as string;
  531. var exSource = null as string;
  532. var exHelpLink = null as string;
  533. exMessage = response.Exception.Message;
  534. exStackTrace = response.Exception.StackTrace;
  535. exSource = response.Exception.Source;
  536. exHelpLink = response.Exception.HelpLink;
  537. // Exception 对象的主要属性。
  538. var exJson = Json.NewObject();
  539. exJson.SetProperty("type", response.Exception.GetType().FullName);
  540. exJson.SetProperty("message", exMessage);
  541. exJson.SetProperty("stack", Json.Parse((exStackTrace ?? "").Replace("\r", "").Split('\n'), false));
  542. exJson.SetProperty("source", exSource);
  543. exJson.SetProperty("helplink", exHelpLink);
  544. // WebException 附加数据。
  545. var webex = response.Exception as System.Net.WebException;
  546. if (webex != null) exJson.SetProperty("data", Json.Parse(webex.Data));
  547. json.SetProperty("exception", exJson);
  548. }
  549. catch (Exception ex)
  550. {
  551. var exJson = Json.NewObject();
  552. exJson.SetProperty("message", TextUtility.Merge("设置 Exception 时再次发生异常:", ex.Message));
  553. json.SetProperty("exception", exJson);
  554. }
  555. }
  556. // 用户数据。
  557. json.SetProperty("data", response.Data);
  558. var indented = ApiOptions.JsonIndent || response.Indented;
  559. var text = json.ToString(indented);
  560. return text;
  561. }
  562. }
  563. internal sealed class ApiProcessorListener : ApiProcessor
  564. {
  565. private HttpListenerContext Context;
  566. private HttpListenerRequest Request;
  567. private HttpListenerResponse Response;
  568. /// <exception cref="ArgumentNullException"></exception>
  569. public ApiProcessorListener(HttpListenerContext context)
  570. {
  571. if (context == null) throw new ArgumentNullException(nameof(context));
  572. Context = context;
  573. Request = context.Request;
  574. Response = context.Response;
  575. HaveContext = true;
  576. }
  577. protected override object GetContext() => Context;
  578. protected override string GetClientIP() => WebUtility.GetClientIP(Request);
  579. protected override HttpMethod GetMethod() => WebUtility.GetMethod(Request);
  580. protected override Uri GetURL() => WebUtility.GetUrl(Request);
  581. protected override Uri GetReferrer() => Request.UrlReferrer;
  582. protected override StringPairs GetHeaders() => WebUtility.GetHeaders(Request);
  583. protected override StringPairs GetCookies() => WebUtility.GetCookies(Request);
  584. protected override long GetRequestLength() => Request.ContentLength64;
  585. protected override byte[] ReadRequest() => BinaryUtility.Read(Request.InputStream);
  586. protected override void SetCacheControl(int seconds) => WebUtility.SetCacheControl(Response, seconds);
  587. protected override void SetContentType(string value) => Response.ContentType = value ?? "";
  588. protected override void SetContentLength(long value) => AddHeader("Content-Length", value.ToString());
  589. protected override void Redirect(string url) => WebUtility.Redirect(Response, url);
  590. protected override string AddHeader(string name, string value) => WebUtility.AddHeader(Response, name, value);
  591. protected override void WriteResponse(byte[] bytes) => BinaryUtility.Write(Response.OutputStream, bytes);
  592. protected override void WriteResponse(Stream stream) => BinaryUtility.Read(stream, Response.OutputStream);
  593. }
  594. #if NETFX
  595. internal sealed class ApiProcessorIIS : ApiProcessor
  596. {
  597. private System.Web.HttpContext Context;
  598. private System.Web.HttpRequest Request;
  599. private System.Web.HttpResponse Response;
  600. /// <exception cref="ArgumentNullException"></exception>
  601. public ApiProcessorIIS(HttpContext context)
  602. {
  603. if (context == null) context = HttpContext.Current;
  604. if (context == null) throw new ArgumentNullException(nameof(context));
  605. Context = context;
  606. Request = context.Request;
  607. Response = context.Response;
  608. HaveContext = true;
  609. }
  610. protected override object GetContext() => Context;
  611. protected override string GetClientIP() => WebUtility.GetClientIP(Request);
  612. protected override HttpMethod GetMethod() => WebUtility.GetMethod(Request);
  613. protected override Uri GetURL() => WebUtility.GetUrl(Request);
  614. protected override Uri GetReferrer() => Request.UrlReferrer;
  615. protected override StringPairs GetHeaders() => WebUtility.GetHeaders(Request);
  616. protected override StringPairs GetCookies() => WebUtility.GetCookies(Request);
  617. protected override long GetRequestLength() => Request.ContentLength;
  618. protected override byte[] ReadRequest() => BinaryUtility.Read(Request.InputStream);
  619. protected override void SetCacheControl(int seconds) => WebUtility.SetCacheControl(Response, seconds);
  620. protected override void SetContentType(string value) => Response.ContentType = value ?? "";
  621. protected override void SetContentLength(long value) => AddHeader("Content-Length", value.ToString());
  622. protected override void Redirect(string url) => WebUtility.Redirect(Response, url);
  623. protected override string AddHeader(string name, string value) => WebUtility.AddHeader(Response, name, value);
  624. protected override void WriteResponse(byte[] bytes)
  625. {
  626. BinaryUtility.Write(Context.Response.OutputStream, bytes);
  627. }
  628. protected override void WriteResponse(Stream stream)
  629. {
  630. BinaryUtility.Read(stream, Context.Response.OutputStream);
  631. }
  632. }
  633. #endif
  634. #if NETCORE
  635. internal sealed class ApiProcessorCore : ApiProcessor
  636. {
  637. private bool AllowSynchronousIO = true;
  638. private Microsoft.AspNetCore.Http.HttpContext Context;
  639. private Microsoft.AspNetCore.Http.HttpRequest Request;
  640. private Microsoft.AspNetCore.Http.HttpResponse Response;
  641. /// <exception cref="ArgumentNullException"></exception>
  642. public ApiProcessorCore(HttpContext context)
  643. {
  644. if (context == null) throw new ArgumentNullException(nameof(context));
  645. Context = context;
  646. Request = context.Request;
  647. Response = context.Response;
  648. HaveContext = true;
  649. }
  650. protected override object GetContext() => Context;
  651. protected override string GetClientIP() => WebUtility.GetClientIP(Request);
  652. protected override HttpMethod GetMethod() => WebUtility.GetMethod(Request);
  653. protected override Uri GetURL() => WebUtility.GetUrl(Request);
  654. protected override Uri GetReferrer() => null;
  655. protected override StringPairs GetHeaders() => WebUtility.GetHeaders(Request);
  656. protected override StringPairs GetCookies() => WebUtility.GetCookies(Request);
  657. protected override long GetRequestLength() => Request.ContentLength ?? -1;
  658. protected override byte[] ReadRequest() => BinaryUtility.Read(Request.Body);
  659. protected override void SetCacheControl(int seconds) => WebUtility.SetCacheControl(Response, seconds);
  660. protected override void SetContentType(string value) => Response.ContentType = value ?? "";
  661. protected override void SetContentLength(long value) => AddHeader("Content-Length", value.ToString());
  662. protected override void Redirect(string url) => WebUtility.Redirect(Response, url);
  663. protected override string AddHeader(string name, string value) => WebUtility.AddHeader(Response, name, value);
  664. protected override void WriteResponse(byte[] bytes)
  665. {
  666. var body = Context.Response.Body;
  667. // sync
  668. BinaryUtility.Write(body, bytes);
  669. // async
  670. // body.BeginWrite(bytes, 0, bytes.Length, (ar) => body.EndWrite(ar), null);
  671. }
  672. protected override void WriteResponse(Stream stream)
  673. {
  674. var body = Context.Response.Body;
  675. // sync
  676. BinaryUtility.Read(stream, body);
  677. // async
  678. }
  679. private static void ReadAsync(Stream source, Stream destination, Func<long, bool> progress = null, int buffer = 4096, Action after = null)
  680. {
  681. if (source == null || !source.CanRead || destination == null || !destination.CanWrite)
  682. {
  683. after?.Invoke();
  684. return;
  685. }
  686. ReadAsync(source, destination, progress, buffer, after, 0L);
  687. }
  688. private static void ReadAsync(Stream source, Stream destination, Func<long, bool> progress, int buffer, Action after, long total)
  689. {
  690. // 缓冲区。
  691. var limit = buffer < 1 ? 1 : buffer;
  692. var temp = new byte[limit];
  693. // 读取一次。
  694. try
  695. {
  696. source.BeginRead(temp, 0, limit, (ar1) =>
  697. {
  698. var count = source.EndRead(ar1);
  699. if (count > 0)
  700. {
  701. destination.BeginWrite(temp, 0, count, (ar2) =>
  702. {
  703. destination.EndWrite(ar2);
  704. total += count;
  705. if (progress != null)
  706. {
  707. var @continue = progress.Invoke(total);
  708. if (!@continue)
  709. {
  710. after?.Invoke();
  711. return;
  712. }
  713. }
  714. ReadAsync(source, destination, progress, buffer, after, total);
  715. }, null);
  716. }
  717. else after?.Invoke();
  718. }, null);
  719. }
  720. catch { }
  721. }
  722. }
  723. #endif
  724. }