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.

321 lines
10 KiB

  1. using Apewer.Network;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Net;
  5. using System.Reflection;
  6. #if NETFX
  7. using System.Web;
  8. #endif
  9. #if NETCORE
  10. using Microsoft.AspNetCore.Http;
  11. using Microsoft.AspNetCore.Hosting;
  12. using Microsoft.Extensions.Hosting;
  13. #endif
  14. namespace Apewer.Web
  15. {
  16. /// <summary>API 调用器。</summary>
  17. public sealed class ApiInvoker
  18. {
  19. private static ApiInvoker _default = new ApiInvoker();
  20. /// <summary>默认的调用器。</summary>
  21. public static ApiInvoker Default { get { return _default; } }
  22. #region instance
  23. private Dictionary<string, ApiApplication> _entries = null;
  24. /// <summary>获取 API 入口的数量。</summary>
  25. public int Entries { get { return _entries.Count; } }
  26. private void AddEntries(Dictionary<string, ApiApplication> entries)
  27. {
  28. if (entries == null || entries.Count < 0) return;
  29. var olds = _entries;
  30. if (olds != null)
  31. {
  32. foreach (var old in olds)
  33. {
  34. if (entries.ContainsKey(old.Key)) continue;
  35. entries.Add(old.Key, old.Value);
  36. }
  37. }
  38. var sorted = Sort(entries);
  39. _entries = sorted;
  40. }
  41. private void AddCurrent() => AddEntries(AppDomain.CurrentDomain.GetAssemblies());
  42. /// <summary>清除所有入口。</summary>
  43. public void ClearEntries() { _entries = null; }
  44. /// <summary>添加指定程序集的入口。</summary>
  45. public void AddEntries(IEnumerable<Assembly> assemblies) => AddEntries(GetEntries(assemblies));
  46. /// <summary>添加指定程序集的入口。</summary>
  47. public void AddEntries(Assembly assembly) => AddEntries(GetEntries(assembly));
  48. private string Invoke(object context, Func<ApiProcessor> func)
  49. {
  50. if (context == null) return "Context 无效。";
  51. if (_entries == null) AddEntries(AppDomain.CurrentDomain.GetAssemblies());
  52. var entries = _entries;
  53. // if (entries == null) return "Entries 无效。";
  54. var processor = func();
  55. processor.Invoker = this;
  56. processor.Entries = entries;
  57. return processor.Run();
  58. }
  59. /// <summary>执行。</summary>
  60. public string Invoke(HttpListenerContext context) => Invoke(context, () => new ApiProcessorListener(context));
  61. /// <summary>从 Listener 发起 API 调用。</summary>
  62. public static string Listener(HttpListenerContext context) => _default.Invoke(context);
  63. #if NETFX
  64. /// <summary>执行。</summary>
  65. public string Invoke(System.Web.HttpContext context) => Invoke(context, () => new ApiProcessorIIS(context));
  66. /// <summary>从 IIS 发起 API 调用。</summary>
  67. public static string IIS(HttpContext context) => _default.Invoke(context);
  68. #endif
  69. #if NETCORE
  70. /// <summary>执行。</summary>
  71. public string Invoke(Microsoft.AspNetCore.Http.HttpContext context) => Invoke(context, () => new ApiProcessorCore(context));
  72. /// <summary>从 ASP.NET Core 发起 API 调用。</summary>
  73. public static string AspNetCore(HttpContext context) => _default.Invoke(context);
  74. #endif
  75. #endregion
  76. #region entries
  77. static Dictionary<string, ApiApplication> Sort(Dictionary<string, ApiApplication> entries)
  78. {
  79. var names = new List<string>();
  80. names.AddRange(entries.Keys);
  81. names.Sort();
  82. var sorted = new Dictionary<string, ApiApplication>();
  83. foreach (var name in names) sorted.Add(name, entries[name]);
  84. return sorted;
  85. }
  86. static Dictionary<string, ApiApplication> GetEntries(Assembly assembly)
  87. {
  88. if (assembly == null) return new Dictionary<string, ApiApplication>();
  89. var types = RuntimeUtility.GetTypes(assembly);
  90. return GetEntries(types);
  91. }
  92. static Dictionary<string, ApiApplication> GetEntries(IEnumerable<Assembly> assemblies)
  93. {
  94. var deduplicates = new List<Assembly>();
  95. foreach (var assembly in assemblies)
  96. {
  97. if (assembly == null) continue;
  98. if (deduplicates.Contains(assembly)) continue;
  99. deduplicates.Add(assembly);
  100. }
  101. var all = new Dictionary<string, ApiApplication>();
  102. foreach (var assembly in deduplicates)
  103. {
  104. var types = RuntimeUtility.GetTypes(assembly);
  105. var entries = GetEntries(types);
  106. foreach (var entry in entries)
  107. {
  108. if (all.ContainsKey(entry.Key)) continue;
  109. all.Add(entry.Key, entry.Value);
  110. }
  111. }
  112. return all;
  113. }
  114. static Dictionary<string, ApiApplication> GetEntries(IEnumerable<Type> types)
  115. {
  116. if (types == null) return null;
  117. var entries = new Dictionary<string, ApiApplication>();
  118. foreach (var type in types)
  119. {
  120. var entry = GetEntry(type);
  121. if (entry == null) continue;
  122. if (entries.ContainsKey(entry.Name)) continue;
  123. entries.Add(entry.Name, entry);
  124. }
  125. return entries;
  126. }
  127. static ApiApplication GetEntry(Type type)
  128. {
  129. var application = GetApplication(type);
  130. if (application == null) return null;
  131. var methods = type.GetMethods();
  132. var fes = new ObjectSet<ApiFunction>(true);
  133. foreach (var method in methods)
  134. {
  135. var function = GetFunction(application, method);
  136. if (function == null) continue;
  137. fes[function.Name] = function;
  138. }
  139. var fns = new List<string>();
  140. fns.AddRange(RuntimeUtility.GetOrigin(fes).Keys);
  141. fns.Sort();
  142. foreach (var i in fns) application.Functions.Add(i, fes[i]);
  143. return application;
  144. }
  145. internal static ApiApplication GetApplication(Type type, bool checkAttribute = true)
  146. {
  147. if (type == null) return null;
  148. // Check Type Properties
  149. if (!type.IsClass) return null;
  150. if (type.IsAbstract) return null;
  151. if (type.IsGenericType) return null;
  152. // Check Type ApiAttributes
  153. var attributes = null as object[];
  154. if (checkAttribute)
  155. {
  156. attributes = type.GetCustomAttributes(typeof(ApiAttribute), false);
  157. if (attributes.Length < 1) return null;
  158. }
  159. // Check Base
  160. if (!RuntimeUtility.IsInherits(type, typeof(ApiController))) return null;
  161. // Entry
  162. var entry = new ApiApplication();
  163. entry.Type = type;
  164. entry.Assembly = type.Assembly;
  165. if (checkAttribute)
  166. {
  167. // Name
  168. var api = (ApiAttribute)attributes[0];
  169. var name = api.Name;
  170. if (string.IsNullOrEmpty(name)) name = type.Name;
  171. name = name.ToLower();
  172. entry.Name = name;
  173. // Caption
  174. entry.Caption = api.Caption;
  175. if (entry.Caption.Length < 1)
  176. {
  177. var captions = type.GetCustomAttributes(typeof(CaptionAttribute), true);
  178. if (captions.Length > 0)
  179. {
  180. var caption = (CaptionAttribute)captions[0];
  181. entry.Caption = caption.Title;
  182. entry.Description = caption.Description;
  183. }
  184. }
  185. // Visible
  186. entry.Visible = api.Visible;
  187. if (entry.Visible)
  188. {
  189. if (type.ContainsAttribute<HiddenAttribute>(false))
  190. {
  191. entry.Visible = false;
  192. }
  193. }
  194. }
  195. else
  196. {
  197. entry.Name = "";
  198. entry.Caption = "";
  199. entry.Visible = false;
  200. }
  201. // Independent
  202. if (type.ContainsAttribute<IndependentAttribute>(false))
  203. {
  204. entry.Independent = true;
  205. }
  206. // Module
  207. entry.Module = TextUtility.Join("-", entry.Assembly.GetName().Name, entry.Assembly.GetName().Version.ToString());
  208. return entry;
  209. }
  210. static ApiFunction GetFunction(ApiApplication application, MethodInfo method)
  211. {
  212. if (method == null) return null;
  213. // 滤除构造函数。
  214. if (method.IsConstructor) return null;
  215. // 滤除继承的方法。
  216. if (method.DeclaringType.Equals(typeof(object))) return null;
  217. if (method.DeclaringType.Equals(typeof(ApiController))) return null;
  218. // 滤除带参数的方法。
  219. var parameters = method.GetParameters();
  220. if (parameters.Length > 0) return null;
  221. // Entry
  222. var entry = new ApiFunction();
  223. entry.Type = application.Type;
  224. entry.Assembly = application.Type.Assembly;
  225. entry.Method = method;
  226. entry.Name = method.Name.ToLower();
  227. // Caption
  228. var captions = method.GetCustomAttributes(typeof(CaptionAttribute), true);
  229. if (captions.Length > 0)
  230. {
  231. var caption = (CaptionAttribute)captions[0];
  232. entry.Caption = caption.Title;
  233. entry.Description = caption.Description;
  234. }
  235. // Visible
  236. entry.Visible = true;
  237. if (application.Visible)
  238. {
  239. var hidden = method.GetCustomAttributes(typeof(HiddenAttribute), false);
  240. if (hidden.Length > 0) entry.Visible = false;
  241. }
  242. else
  243. {
  244. entry.Visible = false;
  245. }
  246. // Returnable
  247. if (method.ReturnType.Equals(typeof(string)))
  248. {
  249. entry.Returnable = true;
  250. }
  251. return entry;
  252. }
  253. #endregion
  254. }
  255. }