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.

841 lines
30 KiB

4 years ago
4 years ago
2 years ago
4 years ago
3 years ago
2 years ago
3 years ago
2 years ago
4 years ago
2 years ago
4 years ago
2 years ago
4 years ago
3 years ago
2 years ago
4 years ago
3 years ago
2 years ago
3 years ago
4 years ago
4 years ago
4 years ago
2 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
4 years ago
2 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
2 years ago
4 years ago
2 years ago
2 years ago
4 years ago
2 years ago
2 years ago
4 years ago
4 years ago
4 years ago
4 years ago
2 years ago
4 years ago
4 years ago
2 years ago
4 years ago
2 years ago
4 years ago
2 years ago
4 years ago
2 years ago
4 years ago
2 years ago
4 years ago
3 years ago
4 years ago
2 years ago
3 years ago
4 years ago
2 years ago
4 years ago
2 years ago
4 years ago
2 years ago
4 years ago
2 years ago
4 years ago
2 years ago
4 years ago
2 years ago
4 years ago
2 years ago
4 years ago
2 years ago
4 years ago
2 years ago
4 years ago
2 years ago
4 years ago
2 years ago
4 years ago
4 years ago
3 years ago
4 years ago
2 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
2 years ago
4 years ago
2 years ago
2 years ago
4 years ago
2 years ago
2 years ago
4 years ago
3 years ago
4 years ago
4 years ago
  1. using Apewer.Internals;
  2. using Apewer.Network;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Net;
  6. using System.Reflection;
  7. using static Apewer.Web.ApiUtility;
  8. namespace Apewer.Web
  9. {
  10. internal class ApiProcessor
  11. {
  12. private ApiContext _context = null;
  13. internal ApiProcessor(ApiContext context) => _context = context ?? throw new ArgumentNullException(nameof(context));
  14. #region prepare
  15. /// <summary>执行处理程序,返回错误信息。</summary>
  16. public void Run()
  17. {
  18. var url = null as Uri;
  19. var method = HttpMethod.NULL;
  20. var response = null as ApiResponse;
  21. try
  22. {
  23. // 检查执行的前提条件,获取 Method 和 URL。
  24. var check = Check(ref method, ref url);
  25. if (!string.IsNullOrEmpty(check))
  26. {
  27. Logger.Internals.Error(typeof(ApiInvoker), check);
  28. return;
  29. }
  30. // 准备请求模型。
  31. var request = GetRequest(_context.Provider, _context.Options, method, url);
  32. _context.Request = request;
  33. // 准备响应模型。
  34. response = new ApiResponse();
  35. response.Random = request.Random;
  36. response.Application = request.Application;
  37. response.Function = request.Function;
  38. _context.Response = response;
  39. // OPTIONS
  40. if (_context.Request.Method == HttpMethod.OPTIONS)
  41. {
  42. if (!_context.Options.AllowOptions)
  43. {
  44. _context.Response.Model = new ApiTextModel("");
  45. return;
  46. }
  47. }
  48. // 中间件
  49. var middlewares = _context.Invoker.Middlewares;
  50. if (middlewares.Length > 0)
  51. {
  52. // 设置队列和回调。
  53. _mw_queue = new Queue<ApiMiddleware>(middlewares);
  54. _context.SetMiddlewareCallback(MiddlewareNext);
  55. // 执行。
  56. MiddlewareNext(_context);
  57. }
  58. else
  59. {
  60. // 无中间件。
  61. Route();
  62. }
  63. }
  64. catch (Exception ex)
  65. {
  66. ApiUtility.Exception(_context.Response, ex, _context.Options.WithException);
  67. var message = ex.Message();
  68. Logger.Internals.Error(typeof(ApiInvoker), message);
  69. }
  70. finally
  71. {
  72. // 输出。
  73. if (response != null)
  74. {
  75. try
  76. {
  77. response.Duration = Duration(_context.Beginning);
  78. Output(_context.Provider, _context.Options, response, null, method);
  79. }
  80. catch { }
  81. finally
  82. {
  83. RuntimeUtility.Dispose(response.Model);
  84. }
  85. }
  86. }
  87. }
  88. static string Duration(DateTime beginning)
  89. {
  90. var span = DateTime.Now - beginning;
  91. var ms = span.TotalMilliseconds;
  92. if (ms < 1000) return Math.Round(ms, 0).ToString() + "ms";
  93. if (ms < 10000) return Math.Round(ms / 1000, 2).ToString() + "s";
  94. if (ms < 60000) return Math.Round(ms / 1000, 1).ToString() + "s";
  95. return Math.Round(ms / 1000, 0).ToString() + "s";
  96. }
  97. string Check(ref HttpMethod method, ref Uri url)
  98. {
  99. // 服务程序检查。
  100. var check = _context.Provider.PreInvoke();
  101. if (!string.IsNullOrEmpty(check)) return check;
  102. // URL
  103. url = _context.Provider.GetUrl();
  104. if (url == null) return "URL 无效。";
  105. // Method
  106. method = _context.Provider.GetMethod();
  107. switch (method)
  108. {
  109. case HttpMethod.NULL:
  110. return "HTTP 方法无效。";
  111. case HttpMethod.OPTIONS:
  112. if (!_context.Options.AllowOptions) return null;
  113. break;
  114. }
  115. // favicon.ico
  116. var lowerPath = TextUtility.AssureStarts(TextUtility.Lower(url.AbsolutePath), "/");
  117. if (!_context.Options.AllowFavIcon)
  118. {
  119. if (lowerPath.StartsWith("/favicon.ico"))
  120. {
  121. Output(_context.Provider, _context.Options, null, null, null);
  122. return "已取消对 favicon.ico 的请求。";
  123. }
  124. }
  125. // robots.txt
  126. if (!_context.Options.AllowRobots)
  127. {
  128. if (lowerPath.StartsWith("/robots.txt"))
  129. {
  130. const string text = "User-agent: *\nDisallow: / \n";
  131. Output(_context.Provider, _context.Options, null, "text/plain", TextUtility.Bytes(text));
  132. return "已取消对 robots.txt 的请求。";
  133. }
  134. }
  135. return null;
  136. }
  137. #endregion
  138. #region common
  139. static Type Void = typeof(void);
  140. // 创建控制器实例
  141. static ApiController CreateController(Type type, ApiContext context)
  142. {
  143. var controller = (ApiController)Activator.CreateInstance(type);
  144. ApiUtility.SetContext(controller, context);
  145. return controller;
  146. }
  147. static void Invoke(ApiContext context, MethodInfo method, ApiParameter[] parameters)
  148. {
  149. context.MethodInfo = method;
  150. // 调用。
  151. var parametersValue = ReadParameters(context.Request, parameters);
  152. var controller = context.Controller;
  153. var returnValue = method.Invoke(controller, parametersValue);
  154. // 程序要求停止输出。
  155. var response = context.Response;
  156. if (response.StopReturn) return;
  157. // 已经有了返回模型。
  158. if (response.Model != null) return;
  159. // 没有返回类型。
  160. var returnType = method.ReturnType;
  161. if (returnType == null || returnType.Equals(Void)) return;
  162. // 已明确字符串类型。
  163. if (returnType.Equals(typeof(string)))
  164. {
  165. var textValue = returnValue as string;
  166. var textRenderer = context.Options.TextRenderer;
  167. if (textRenderer != null)
  168. {
  169. textRenderer.Invoke(context, textValue);
  170. return;
  171. }
  172. // 默认视为提示错误
  173. if (!string.IsNullOrEmpty(textValue)) response.Error(textValue);
  174. return;
  175. }
  176. // 已明确 Exception 类型,视为提示错误。
  177. if (returnValue is Exception)
  178. {
  179. ApiUtility.Exception(response, returnValue as Exception);
  180. return;
  181. }
  182. // 已明确 Json 类型。
  183. if (returnValue is Json json)
  184. {
  185. var renderer = context.Options.JsonRenderer;
  186. if (renderer != null)
  187. {
  188. renderer.Invoke(context, json);
  189. return;
  190. }
  191. // 默认设置到 data 属性。
  192. response.Data = json;
  193. return;
  194. }
  195. // 已明确 Model 类型。
  196. if (returnValue is IApiModel model)
  197. {
  198. response.Model = model;
  199. return;
  200. }
  201. // 已明确 Result 类型。
  202. if (returnValue is IActionResult result)
  203. {
  204. response.Model = result;
  205. return;
  206. }
  207. // 类型未知,尝试 ToJson 方法。
  208. if (returnValue is IToJson toJson)
  209. {
  210. var tojson = toJson.ToJson();
  211. var renderer = context.Options.JsonRenderer;
  212. if (renderer != null)
  213. {
  214. renderer.Invoke(context, tojson);
  215. return;
  216. }
  217. response.Data = tojson;
  218. return;
  219. }
  220. // 未知返回类型,尝试使用默认渲染器。
  221. var defaultRenderer = context.Options.DefaultRenderer;
  222. if (defaultRenderer != null) defaultRenderer.Invoke(context, returnValue);
  223. }
  224. #endregion
  225. #region middleware
  226. Queue<ApiMiddleware> _mw_queue = null;
  227. void MiddlewareNext(ApiContext context)
  228. {
  229. if (_mw_queue.Count < 1)
  230. {
  231. Route();
  232. return;
  233. }
  234. // 创建下一个中间件的实例
  235. var middleware = _mw_queue.Dequeue();
  236. if (middleware.Type != null)
  237. {
  238. var instance = Activator.CreateInstance(middleware.Type) as IApiMiddleware;
  239. if (instance == null) throw new Exception($"类型【{middleware.Type.FullName}】不是有效的中间件。");
  240. // 调用
  241. instance.Invoke(context);
  242. }
  243. else if (middleware.Callback != null)
  244. {
  245. middleware.Callback.Invoke(context, () => MiddlewareNext(context));
  246. }
  247. else
  248. {
  249. MiddlewareNext(context);
  250. }
  251. }
  252. // 执行路由。
  253. void Route()
  254. {
  255. // 路由
  256. if (_context.Options.UseRoute)
  257. {
  258. var path = _context?.Request?.Url?.AbsolutePath;
  259. path = path.TrimEnd('/');
  260. var action = _context.Entries.GetAction(path);
  261. if (action != null)
  262. {
  263. _context.ApiAction = action;
  264. if (action.Unparallel)
  265. {
  266. UnparallelLocker.InLock(GetUnparallelKey(action.MethodInfo), () =>
  267. {
  268. InvokeAction(action);
  269. });
  270. }
  271. else
  272. {
  273. InvokeAction(action);
  274. }
  275. return;
  276. }
  277. }
  278. // 反射
  279. if (_context.Options.UseReflection)
  280. {
  281. var appName = _context.Request.Application;
  282. var application = _context.Entries.GetApplication(appName);
  283. InvokeApplication(application);
  284. return;
  285. }
  286. // 未匹配到
  287. _context.Response.Duration = Duration(_context.Beginning);
  288. _context.Response.Model = new ApiStatusModel(404);
  289. }
  290. #endregion
  291. #region route
  292. // 执行 Action。
  293. void InvokeAction(ApiAction action)
  294. {
  295. var controller = null as ApiController;
  296. try
  297. {
  298. // 准备控制器。
  299. controller = CreateController(action.Type, _context);
  300. // 准备参数。
  301. var parameters = action.Parameters;
  302. var values = ReadParameters(_context.Request, parameters);
  303. // 调用。
  304. _context.Controller = controller;
  305. Invoke(_context, action.MethodInfo, action.Parameters);
  306. }
  307. catch (Exception ex)
  308. {
  309. if (ex.InnerException != null) ex = ex.InnerException;
  310. ApiUtility.Exception(_context.Response, ex, _context.Options.WithException);
  311. var catcher = _context.Invoker.Catcher;
  312. if (catcher != null)
  313. {
  314. try
  315. {
  316. var apiCatch = new ApiCatch(_context, ex);
  317. catcher.Invoke(apiCatch);
  318. }
  319. catch { }
  320. }
  321. }
  322. finally
  323. {
  324. RuntimeUtility.Dispose(controller);
  325. }
  326. }
  327. #endregion
  328. #region reflection
  329. // 创建控制器。
  330. void InvokeApplication(ApiApplication application)
  331. {
  332. var options = _context.Options;
  333. var entries = _context.Entries;
  334. var request = _context.Request;
  335. var response = _context.Response;
  336. // Application 无效,尝试默认控制器和枚举。
  337. if (application == null)
  338. {
  339. var @default = options.Default;
  340. if (@default == null)
  341. {
  342. // 没有指定默认控制器,尝试枚举。
  343. response.Status = "notfound";
  344. response.Message = "Not Found";
  345. if (options.AllowEnumerate) response.Data = Enumerate(entries.Applications, options);
  346. return;
  347. }
  348. else
  349. {
  350. // 创建默认控制器。
  351. var controller = null as ApiController;
  352. try
  353. {
  354. controller = CreateController(@default, _context);
  355. InvokeFunction(controller, application, null, options, request, response);
  356. }
  357. catch (Exception ex)
  358. {
  359. ApiUtility.Exception(response, ex.InnerException ?? ex);
  360. }
  361. finally
  362. {
  363. RuntimeUtility.Dispose(controller);
  364. }
  365. }
  366. }
  367. else
  368. {
  369. // 创建控制器时候会填充 Controller.Request 属性,可能导致 Request.Function 被篡改,所以在创建之前获取 Function。
  370. var function = application.GetFunction(request.Function);
  371. var controller = null as ApiController;
  372. try
  373. {
  374. controller = CreateController(application.Type, _context);
  375. InvokeFunction(controller, application, function, options, request, response);
  376. }
  377. catch (Exception ex)
  378. {
  379. ApiUtility.Exception(response, ex.InnerException ?? ex);
  380. }
  381. finally
  382. {
  383. RuntimeUtility.Dispose(controller);
  384. }
  385. }
  386. }
  387. // 调用 Function。
  388. void InvokeFunction(ApiController controller, ApiApplication application, ApiFunction function, ApiOptions options, ApiRequest request, ApiResponse response)
  389. {
  390. try
  391. {
  392. // 控制器初始化。
  393. var initializer = ApiUtility.GetInitialier(controller);
  394. if (initializer != null)
  395. {
  396. var @continue = true;
  397. @continue = initializer.Invoke(controller);
  398. if (!@continue) return;
  399. }
  400. // 此应用独立处理,不受反射控制。
  401. if (application.Independent) return;
  402. if (function != null)
  403. {
  404. // 调用 API,获取返回值。
  405. _context.Controller = controller;
  406. Invoke(_context, function.Method, function.Parameters);
  407. }
  408. else
  409. {
  410. // 未匹配到 Function,尝试 Default。
  411. var @default = ApiUtility.GetDefault(controller);
  412. if (@default != null)
  413. {
  414. @default.Invoke(controller);
  415. return;
  416. }
  417. // 没有执行任何 Function,尝试枚举。
  418. response.Status = "notfound";
  419. if (application.Hidden)
  420. {
  421. response.Message = "Not Found";
  422. }
  423. else
  424. {
  425. response.Message = "Not Found";
  426. if (options.AllowEnumerate) response.Data = Enumerate(application.Functions, options);
  427. }
  428. }
  429. }
  430. catch (Exception ex)
  431. {
  432. if (ex.InnerException != null) ex = ex.InnerException;
  433. ApiUtility.Exception(_context.Response, ex, _context.Options.WithException);
  434. var catcher = _context.Invoker.Catcher;
  435. if (catcher != null)
  436. {
  437. try
  438. {
  439. var apiCatch = new ApiCatch(_context, ex);
  440. catcher.Invoke(apiCatch);
  441. }
  442. catch { }
  443. }
  444. }
  445. }
  446. #endregion
  447. #region static
  448. internal static ApiRequest GetRequest(ApiProvider provider, ApiOptions options, HttpMethod method, Uri url)
  449. {
  450. // 创建数据对象。
  451. var request = new ApiRequest();
  452. // Http Method。
  453. request.Method = method;
  454. // 基本信息。
  455. var ip = provider.GetClientIP();
  456. var headers = provider.GetHeaders() ?? new HttpHeaders();
  457. request.Headers = headers;
  458. request.IP = ip;
  459. request.Url = url;
  460. request.Referrer = provider.GetReferrer();
  461. request.Parameters = ApiUtility.Parameters(url.Query);
  462. // Headers。
  463. request.UserAgent = ApiUtility.UserAgent(headers);
  464. request.Cookies = ParseCookies(headers) ?? new CookieCollection();
  465. // 匹配 API。
  466. var application = null as string;
  467. var function = null as string;
  468. var random = null as string;
  469. var ticket = null as string;
  470. var session = null as string;
  471. var page = null as string;
  472. // 解析 POST 请求。
  473. switch (request.Method)
  474. {
  475. case HttpMethod.PATCH:
  476. case HttpMethod.POST:
  477. case HttpMethod.PUT:
  478. var preRead = provider.PreRead();
  479. if (string.IsNullOrEmpty(preRead))
  480. {
  481. var post = null as byte[];
  482. var length = 0L;
  483. var max = options.MaxRequestBody;
  484. if (max == 0) post = new byte[0];
  485. else if (max < 0) post = provider.RequestBody().Read();
  486. else
  487. {
  488. length = provider.GetContentLength();
  489. if (length <= max) post = provider.RequestBody().Read();
  490. }
  491. length = post == null ? 0 : post.Length;
  492. if (length > 1)
  493. {
  494. request.PostData = post;
  495. if (length < 104857600)
  496. {
  497. var text = TextUtility.FromBytes(post);
  498. request.PostText = text;
  499. // 尝试解析 Json,首尾必须是“{}”或“[]”。
  500. var first = post[0];
  501. var last = post[length - 1];
  502. if ((first == 123 && last == 125) || (first == 91 && last == 93))
  503. {
  504. var json = Json.From(text);
  505. if (json != null && json.IsObject)
  506. {
  507. application = json["application"];
  508. function = json["function"];
  509. random = json["random"];
  510. ticket = json["ticket"];
  511. session = json["session"];
  512. page = json["page"];
  513. var data = json.GetProperty("data");
  514. request.PostJson = json;
  515. request.Data = data ?? Json.NewObject();
  516. }
  517. }
  518. // 尝试解析 Form,需要 application/x-www-form-urlencoded
  519. var contentType = headers.GetValue("Content-Type") ?? "";
  520. if (contentType.Contains("urlencoded")) request.Form = ApiUtility.Parameters(text);
  521. }
  522. }
  523. }
  524. break;
  525. }
  526. // 解析 URL 参数。
  527. // URL 参数的优先级应高于 URL 路径,以避免反向代理产生的路径问题。
  528. var urlParameters = ApiUtility.Parameters(request.Url.Query);
  529. if (string.IsNullOrEmpty(application)) application = urlParameters.GetValue("application");
  530. if (string.IsNullOrEmpty(function)) function = urlParameters.GetValue("function");
  531. if (string.IsNullOrEmpty(random)) random = urlParameters.GetValue("random");
  532. if (string.IsNullOrEmpty(ticket)) ticket = urlParameters.GetValue("ticket");
  533. if (string.IsNullOrEmpty(session)) session = urlParameters.GetValue("session");
  534. if (string.IsNullOrEmpty(page)) page = urlParameters.GetValue("page");
  535. // 从 Cookie 中获取 Ticket。
  536. var cookies = request.Cookies;
  537. if (string.IsNullOrEmpty(ticket)) ticket = cookies.GetValue("ticket");
  538. // 最后检查 URL 路径。
  539. var paths = (request.Url.AbsolutePath ?? "").Split('/');
  540. if (string.IsNullOrEmpty(application) && paths.Length >= 2) application = TextUtility.DecodeUrl(paths[1]);
  541. if (string.IsNullOrEmpty(function) && paths.Length >= 3) function = TextUtility.DecodeUrl(paths[2]);
  542. // 修正内容。
  543. application = TextUtility.Trim(application);
  544. function = TextUtility.Trim(function);
  545. random = TextUtility.Trim(random);
  546. ticket = TextUtility.Trim(ticket);
  547. session = TextUtility.Trim(session);
  548. page = TextUtility.Trim(page);
  549. // 设置请求:回传。
  550. request.Application = application;
  551. request.Function = function;
  552. request.Random = random;
  553. // 设置请求:不回传。
  554. request.Ticket = ticket;
  555. request.Session = session;
  556. request.Page = page;
  557. return request;
  558. }
  559. static StringPairs PrepareHeaders(ApiOptions options, ApiResponse response, ApiRequest request = null)
  560. {
  561. var merged = new StringPairs();
  562. if (options != null)
  563. {
  564. // 跨域访问。
  565. if (options.WithAccessControl)
  566. {
  567. merged.Add("Access-Control-Allow-Headers", "Content-Type");
  568. merged.Add("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
  569. merged.Add("Access-Control-Allow-Origin", "*");
  570. var maxage = options.AccessControlMaxAge;
  571. if (maxage > 0) merged.Add("Access-Control-Max-Age", maxage.ToString());
  572. if (request != null && request.Headers != null)
  573. {
  574. var @private = request.Headers.GetValue("Access-Control-Request-Private-Network");
  575. if (NumberUtility.Boolean(@private)) merged.Add("Access-Control-Allow-Private-Network", "true");
  576. }
  577. }
  578. // Content-Type 检查。
  579. if (options.WithContentTypeOptions)
  580. {
  581. merged.Add("X-Content-Type-Options", "nosniff");
  582. }
  583. // 用于客户端,当前页面使用 HTTPS 时,将资源升级为 HTTPS。
  584. if (options.UpgradeHttps)
  585. {
  586. merged.Add("Content-Security-Policy", "upgrade-insecure-requests");
  587. }
  588. // 包含 API 的处理时间。
  589. if (options.WithDuration && response != null)
  590. {
  591. if (response.Duration.NotEmpty()) merged.Add("Duration", response.Duration);
  592. }
  593. }
  594. if (response != null)
  595. {
  596. // Cookies。
  597. var setCookies = SetCookie(response.Cookies);
  598. if (setCookies != null)
  599. {
  600. foreach (var value in setCookies) merged.Add("Set-Cookie", value);
  601. }
  602. // 自定义头。
  603. var headers = response.Headers;
  604. if (headers != null)
  605. {
  606. foreach (var header in headers)
  607. {
  608. var key = TextUtility.Trim(header.Name);
  609. if (string.IsNullOrEmpty(key)) continue;
  610. var value = header.Value;
  611. if (string.IsNullOrEmpty(value)) continue;
  612. merged.Add(key, value);
  613. }
  614. }
  615. }
  616. return merged;
  617. }
  618. internal void Output(ApiProvider provider, ApiOptions options, ApiResponse response, string type, byte[] bytes)
  619. {
  620. var preWrite = provider.PreWrite();
  621. if (!string.IsNullOrEmpty(preWrite)) return;
  622. if (response != null)
  623. {
  624. var responsePreOutput = response.PreOutput;
  625. if (responsePreOutput != null)
  626. {
  627. var @continue = responsePreOutput.Invoke(_context);
  628. if (!@continue) return;
  629. }
  630. }
  631. var invokerPreOutput = _context.Invoker.PreOutput;
  632. if (invokerPreOutput != null)
  633. {
  634. var @continue = invokerPreOutput.Invoke(_context);
  635. if (!@continue) return;
  636. }
  637. var optionsPreOutput = _context.Options.PreOutput;
  638. if (optionsPreOutput != null)
  639. {
  640. var @continue = optionsPreOutput.Invoke(_context);
  641. if (!@continue) return;
  642. }
  643. var headers = PrepareHeaders(options, response);
  644. foreach (var header in headers) provider.SetHeader(header.Key, header.Value);
  645. provider.SetCache(0);
  646. provider.SetContentType(string.IsNullOrEmpty(type) ? "application/octet-stream" : type);
  647. var length = bytes == null ? 0 : bytes.Length;
  648. provider.SetContentLength(length);
  649. if (length > 0) provider.ResponseBody().Write(bytes, 0, bytes.Length);
  650. provider.Sent();
  651. }
  652. internal void Output(ApiProvider provider, ApiOptions options, ApiResponse response, ApiRequest request, HttpMethod method)
  653. {
  654. var preWrite = provider.PreWrite();
  655. if (!string.IsNullOrEmpty(preWrite)) return;
  656. if (response != null)
  657. {
  658. var responsePreOutput = response.PreOutput;
  659. if (responsePreOutput != null)
  660. {
  661. var @continue = responsePreOutput.Invoke(_context);
  662. if (!@continue) return;
  663. }
  664. }
  665. var invokerPreOutput = _context.Invoker.PreOutput;
  666. if (invokerPreOutput != null)
  667. {
  668. var @continue = invokerPreOutput.Invoke(_context);
  669. if (!@continue) return;
  670. }
  671. var optionsPreOutput = _context.Options.PreOutput;
  672. if (optionsPreOutput != null)
  673. {
  674. var @continue = optionsPreOutput.Invoke(_context);
  675. if (!@continue) return;
  676. }
  677. // 设置头。
  678. var headers = PrepareHeaders(options, response, request);
  679. foreach (var header in headers) provider.SetHeader(header.Key, header.Value);
  680. // 自定义模型
  681. var model = response.Model as IApiModel;
  682. var result = response.Model as IActionResult;
  683. if (model != null)
  684. {
  685. try
  686. {
  687. model.Output(_context);
  688. }
  689. catch (Exception ex)
  690. {
  691. Logger.Internals.Exception(ex, model);
  692. }
  693. RuntimeUtility.Dispose(model);
  694. return;
  695. }
  696. else if (result != null)
  697. {
  698. try
  699. {
  700. result.ExecuteResult(_context);
  701. }
  702. catch (Exception ex)
  703. {
  704. Logger.Internals.Exception(ex, result);
  705. }
  706. RuntimeUtility.Dispose(result);
  707. return;
  708. }
  709. var text = ApiUtility.ToJson(response, options);
  710. var bytes = TextUtility.Bytes(text);
  711. provider.SetCache(0);
  712. provider.SetContentType("text/json; charset=utf-8");
  713. provider.SetContentLength(bytes.Length);
  714. var stream = provider.ResponseBody();
  715. if (stream != null && stream.CanWrite) stream.Write(bytes, 0, bytes.Length);
  716. provider.Sent();
  717. }
  718. #endregion
  719. #region 不可并行
  720. static TextLocker UnparallelLocker = new TextLocker();
  721. static string GetUnparallelKey(MethodInfo method)
  722. {
  723. return $"{method.DeclaringType.Assembly.ToString()} | {method.DeclaringType.FullName} | {method.ToString()}";
  724. }
  725. static string GetUnparallelKey(Type type)
  726. {
  727. return $"{type.DeclaringType.Assembly.ToString()}";
  728. }
  729. #endregion
  730. }
  731. }