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.

850 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. UnparallelLocker.InLock(GetUnparallelKey(controller.GetType()), () =>
  398. {
  399. @continue = initializer.Invoke(controller);
  400. });
  401. if (!@continue) return;
  402. }
  403. // 此应用独立处理,不受反射控制。
  404. if (application.Independent) return;
  405. if (function != null)
  406. {
  407. // 调用 API,获取返回值。
  408. _context.Controller = controller;
  409. UnparallelLocker.InLock(GetUnparallelKey(function.Method), () =>
  410. {
  411. Invoke(_context, function.Method, function.Parameters);
  412. });
  413. }
  414. else
  415. {
  416. // 未匹配到 Function,尝试 Default。
  417. var @default = ApiUtility.GetDefault(controller);
  418. if (@default != null)
  419. {
  420. UnparallelLocker.InLock(GetUnparallelKey(controller.GetType()), () =>
  421. {
  422. @default.Invoke(controller);
  423. return;
  424. });
  425. }
  426. // 没有执行任何 Function,尝试枚举。
  427. response.Status = "notfound";
  428. if (application.Hidden)
  429. {
  430. response.Message = "Not Found";
  431. }
  432. else
  433. {
  434. response.Message = "Not Found";
  435. if (options.AllowEnumerate) response.Data = Enumerate(application.Functions, options);
  436. }
  437. }
  438. }
  439. catch (Exception ex)
  440. {
  441. if (ex.InnerException != null) ex = ex.InnerException;
  442. ApiUtility.Exception(_context.Response, ex, _context.Options.WithException);
  443. var catcher = _context.Invoker.Catcher;
  444. if (catcher != null)
  445. {
  446. try
  447. {
  448. var apiCatch = new ApiCatch(_context, ex);
  449. catcher.Invoke(apiCatch);
  450. }
  451. catch { }
  452. }
  453. }
  454. }
  455. #endregion
  456. #region static
  457. internal static ApiRequest GetRequest(ApiProvider provider, ApiOptions options, HttpMethod method, Uri url)
  458. {
  459. // 创建数据对象。
  460. var request = new ApiRequest();
  461. // Http Method。
  462. request.Method = method;
  463. // 基本信息。
  464. var ip = provider.GetClientIP();
  465. var headers = provider.GetHeaders() ?? new HttpHeaders();
  466. request.Headers = headers;
  467. request.IP = ip;
  468. request.Url = url;
  469. request.Referrer = provider.GetReferrer();
  470. request.Parameters = ApiUtility.Parameters(url.Query);
  471. // Headers。
  472. request.UserAgent = ApiUtility.UserAgent(headers);
  473. request.Cookies = ParseCookies(headers) ?? new CookieCollection();
  474. // 匹配 API。
  475. var application = null as string;
  476. var function = null as string;
  477. var random = null as string;
  478. var ticket = null as string;
  479. var session = null as string;
  480. var page = null as string;
  481. // 解析 POST 请求。
  482. switch (request.Method)
  483. {
  484. case HttpMethod.PATCH:
  485. case HttpMethod.POST:
  486. case HttpMethod.PUT:
  487. var preRead = provider.PreRead();
  488. if (string.IsNullOrEmpty(preRead))
  489. {
  490. var post = null as byte[];
  491. var length = 0L;
  492. var max = options.MaxRequestBody;
  493. if (max == 0) post = new byte[0];
  494. else if (max < 0) post = provider.RequestBody().Read();
  495. else
  496. {
  497. length = provider.GetContentLength();
  498. if (length <= max) post = provider.RequestBody().Read();
  499. }
  500. length = post == null ? 0 : post.Length;
  501. if (length > 1)
  502. {
  503. request.PostData = post;
  504. if (length < 104857600)
  505. {
  506. var text = TextUtility.FromBytes(post);
  507. request.PostText = text;
  508. // 尝试解析 Json,首尾必须是“{}”或“[]”。
  509. var first = post[0];
  510. var last = post[length - 1];
  511. if ((first == 123 && last == 125) || (first == 91 && last == 93))
  512. {
  513. var json = Json.From(text);
  514. if (json != null && json.IsObject)
  515. {
  516. application = json["application"];
  517. function = json["function"];
  518. random = json["random"];
  519. ticket = json["ticket"];
  520. session = json["session"];
  521. page = json["page"];
  522. var data = json.GetProperty("data");
  523. request.PostJson = json;
  524. request.Data = data ?? Json.NewObject();
  525. }
  526. }
  527. // 尝试解析 Form,需要 application/x-www-form-urlencoded
  528. var contentType = headers.GetValue("Content-Type") ?? "";
  529. if (contentType.Contains("urlencoded")) request.Form = ApiUtility.Parameters(text);
  530. }
  531. }
  532. }
  533. break;
  534. }
  535. // 解析 URL 参数。
  536. // URL 参数的优先级应高于 URL 路径,以避免反向代理产生的路径问题。
  537. var urlParameters = ApiUtility.Parameters(request.Url.Query);
  538. if (string.IsNullOrEmpty(application)) application = urlParameters.GetValue("application");
  539. if (string.IsNullOrEmpty(function)) function = urlParameters.GetValue("function");
  540. if (string.IsNullOrEmpty(random)) random = urlParameters.GetValue("random");
  541. if (string.IsNullOrEmpty(ticket)) ticket = urlParameters.GetValue("ticket");
  542. if (string.IsNullOrEmpty(session)) session = urlParameters.GetValue("session");
  543. if (string.IsNullOrEmpty(page)) page = urlParameters.GetValue("page");
  544. // 从 Cookie 中获取 Ticket。
  545. var cookies = request.Cookies;
  546. if (string.IsNullOrEmpty(ticket)) ticket = cookies.GetValue("ticket");
  547. // 最后检查 URL 路径。
  548. var paths = (request.Url.AbsolutePath ?? "").Split('/');
  549. if (string.IsNullOrEmpty(application) && paths.Length >= 2) application = TextUtility.DecodeUrl(paths[1]);
  550. if (string.IsNullOrEmpty(function) && paths.Length >= 3) function = TextUtility.DecodeUrl(paths[2]);
  551. // 修正内容。
  552. application = TextUtility.Trim(application);
  553. function = TextUtility.Trim(function);
  554. random = TextUtility.Trim(random);
  555. ticket = TextUtility.Trim(ticket);
  556. session = TextUtility.Trim(session);
  557. page = TextUtility.Trim(page);
  558. // 设置请求:回传。
  559. request.Application = application;
  560. request.Function = function;
  561. request.Random = random;
  562. // 设置请求:不回传。
  563. request.Ticket = ticket;
  564. request.Session = session;
  565. request.Page = page;
  566. return request;
  567. }
  568. static StringPairs PrepareHeaders(ApiOptions options, ApiResponse response, ApiRequest request = null)
  569. {
  570. var merged = new StringPairs();
  571. if (options != null)
  572. {
  573. // 跨域访问。
  574. if (options.WithAccessControl)
  575. {
  576. merged.Add("Access-Control-Allow-Headers", "Content-Type");
  577. merged.Add("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
  578. merged.Add("Access-Control-Allow-Origin", "*");
  579. var maxage = options.AccessControlMaxAge;
  580. if (maxage > 0) merged.Add("Access-Control-Max-Age", maxage.ToString());
  581. if (request != null && request.Headers != null)
  582. {
  583. var @private = request.Headers.GetValue("Access-Control-Request-Private-Network");
  584. if (NumberUtility.Boolean(@private)) merged.Add("Access-Control-Allow-Private-Network", "true");
  585. }
  586. }
  587. // Content-Type 检查。
  588. if (options.WithContentTypeOptions)
  589. {
  590. merged.Add("X-Content-Type-Options", "nosniff");
  591. }
  592. // 用于客户端,当前页面使用 HTTPS 时,将资源升级为 HTTPS。
  593. if (options.UpgradeHttps)
  594. {
  595. merged.Add("Content-Security-Policy", "upgrade-insecure-requests");
  596. }
  597. // 包含 API 的处理时间。
  598. if (options.WithDuration && response != null)
  599. {
  600. if (response.Duration.NotEmpty()) merged.Add("Duration", response.Duration);
  601. }
  602. }
  603. if (response != null)
  604. {
  605. // Cookies。
  606. var setCookies = SetCookie(response.Cookies);
  607. if (setCookies != null)
  608. {
  609. foreach (var value in setCookies) merged.Add("Set-Cookie", value);
  610. }
  611. // 自定义头。
  612. var headers = response.Headers;
  613. if (headers != null)
  614. {
  615. foreach (var header in headers)
  616. {
  617. var key = TextUtility.Trim(header.Name);
  618. if (string.IsNullOrEmpty(key)) continue;
  619. var value = header.Value;
  620. if (string.IsNullOrEmpty(value)) continue;
  621. merged.Add(key, value);
  622. }
  623. }
  624. }
  625. return merged;
  626. }
  627. internal void Output(ApiProvider provider, ApiOptions options, ApiResponse response, string type, byte[] bytes)
  628. {
  629. var preWrite = provider.PreWrite();
  630. if (!string.IsNullOrEmpty(preWrite)) return;
  631. if (response != null)
  632. {
  633. var responsePreOutput = response.PreOutput;
  634. if (responsePreOutput != null)
  635. {
  636. var @continue = responsePreOutput.Invoke(_context);
  637. if (!@continue) return;
  638. }
  639. }
  640. var invokerPreOutput = _context.Invoker.PreOutput;
  641. if (invokerPreOutput != null)
  642. {
  643. var @continue = invokerPreOutput.Invoke(_context);
  644. if (!@continue) return;
  645. }
  646. var optionsPreOutput = _context.Options.PreOutput;
  647. if (optionsPreOutput != null)
  648. {
  649. var @continue = optionsPreOutput.Invoke(_context);
  650. if (!@continue) return;
  651. }
  652. var headers = PrepareHeaders(options, response);
  653. foreach (var header in headers) provider.SetHeader(header.Key, header.Value);
  654. provider.SetCache(0);
  655. provider.SetContentType(string.IsNullOrEmpty(type) ? "application/octet-stream" : type);
  656. var length = bytes == null ? 0 : bytes.Length;
  657. provider.SetContentLength(length);
  658. if (length > 0) provider.ResponseBody().Write(bytes, 0, bytes.Length);
  659. provider.Sent();
  660. }
  661. internal void Output(ApiProvider provider, ApiOptions options, ApiResponse response, ApiRequest request, HttpMethod method)
  662. {
  663. var preWrite = provider.PreWrite();
  664. if (!string.IsNullOrEmpty(preWrite)) return;
  665. if (response != null)
  666. {
  667. var responsePreOutput = response.PreOutput;
  668. if (responsePreOutput != null)
  669. {
  670. var @continue = responsePreOutput.Invoke(_context);
  671. if (!@continue) return;
  672. }
  673. }
  674. var invokerPreOutput = _context.Invoker.PreOutput;
  675. if (invokerPreOutput != null)
  676. {
  677. var @continue = invokerPreOutput.Invoke(_context);
  678. if (!@continue) return;
  679. }
  680. var optionsPreOutput = _context.Options.PreOutput;
  681. if (optionsPreOutput != null)
  682. {
  683. var @continue = optionsPreOutput.Invoke(_context);
  684. if (!@continue) return;
  685. }
  686. // 设置头。
  687. var headers = PrepareHeaders(options, response, request);
  688. foreach (var header in headers) provider.SetHeader(header.Key, header.Value);
  689. // 自定义模型
  690. var model = response.Model as IApiModel;
  691. var result = response.Model as IActionResult;
  692. if (model != null)
  693. {
  694. try
  695. {
  696. model.Output(_context);
  697. }
  698. catch (Exception ex)
  699. {
  700. Logger.Internals.Exception(ex, model);
  701. }
  702. RuntimeUtility.Dispose(model);
  703. return;
  704. }
  705. else if (result != null)
  706. {
  707. try
  708. {
  709. result.ExecuteResult(_context);
  710. }
  711. catch (Exception ex)
  712. {
  713. Logger.Internals.Exception(ex, result);
  714. }
  715. RuntimeUtility.Dispose(result);
  716. return;
  717. }
  718. var text = ApiUtility.ToJson(response, options);
  719. var bytes = TextUtility.Bytes(text);
  720. provider.SetCache(0);
  721. provider.SetContentType("text/json; charset=utf-8");
  722. provider.SetContentLength(bytes.Length);
  723. var stream = provider.ResponseBody();
  724. if (stream != null && stream.CanWrite) stream.Write(bytes, 0, bytes.Length);
  725. provider.Sent();
  726. }
  727. #endregion
  728. #region 不可并行
  729. static TextLocker UnparallelLocker = new TextLocker();
  730. static string GetUnparallelKey(MethodInfo method)
  731. {
  732. return $"{method.DeclaringType.Assembly.ToString()} | {method.DeclaringType.FullName} | {method.ToString()}";
  733. }
  734. static string GetUnparallelKey(Type type)
  735. {
  736. return $"{type.DeclaringType.Assembly.ToString()}";
  737. }
  738. #endregion
  739. }
  740. }