BaseHandler.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection.Emit;
  6. using MiniWebApi.Network;
  7. namespace MiniWebApi.Handler
  8. {
  9. public class BaseHandler
  10. {
  11. private readonly Dictionary<string, CallingMethod> _callingMethods = new Dictionary<string, CallingMethod>();
  12. public BaseHandler()
  13. {
  14. RegisterCallingMethods();
  15. }
  16. private void RegisterCallingMethods()
  17. {
  18. //Load all methods.
  19. var methods = GetType().GetMethods();
  20. foreach (var method in methods.Where(x => x.IsVirtual == false && x.IsStatic == false && x.ReturnType == typeof(void)))
  21. {
  22. //Get if the method support WebApiAttribute
  23. var methodWebApiAttrs = method.GetCustomAttributes(typeof(WebApiMethodAttribute), true);
  24. if (methodWebApiAttrs.Length == 0)
  25. {
  26. //If no WebApiAttribute, do not add it into the method dictionary.
  27. continue;
  28. }
  29. if (methodWebApiAttrs.Length >1)
  30. {
  31. // Can not define more than 2 attributes
  32. throw new InvalidOperationException($"Method {method.Name} defined more than one WebApi method attributes.");
  33. }
  34. if (!method.IsPublic)
  35. {
  36. //Web api method should be public
  37. throw new InvalidOperationException($"Method {method.Name} should be public for WebApi method.");
  38. }
  39. var webApiMethodAttribute = (WebApiMethodAttribute)methodWebApiAttrs[0];
  40. var methodWebApiType = webApiMethodAttribute.ToWebApiType();
  41. var methodName = string.IsNullOrWhiteSpace(webApiMethodAttribute.Name)
  42. ? method.Name
  43. : webApiMethodAttribute.Name;
  44. //Get all parameters
  45. var parameters = method.GetParameters();
  46. //If the first parameter is not WebApiHttpContext throw the exception
  47. if (parameters[0].ParameterType != typeof(WebApiHttpContext))
  48. {
  49. throw new InvalidDataException($"The first argument of method {method.Name} must be WebApiHttpContext");
  50. }
  51. //Generate CallingParameters
  52. var paramInfos = new List<CallingParameter>();
  53. foreach (var parameterInfo in parameters)
  54. {
  55. if (parameterInfo.ParameterType == typeof(WebApiHttpContext))
  56. {
  57. continue;
  58. }
  59. var fromType = FromType.None;
  60. //Get if the parameter support WebApiAttribute
  61. var paramWebApiAttrs = parameterInfo.GetCustomAttributes(typeof(WebApiParameterAttribute), true);
  62. if (paramWebApiAttrs.Length > 0)
  63. {
  64. if (paramWebApiAttrs[0] is FromUrlAttribute)
  65. {
  66. fromType = FromType.FromUrl;
  67. }
  68. else if (paramWebApiAttrs[0] is FromBodyAttribute)
  69. {
  70. fromType = FromType.FromBody;
  71. }
  72. }
  73. paramInfos.Add(new CallingParameter(parameterInfo.Name, parameterInfo.ParameterType, fromType));
  74. }
  75. //More than one params' FormType is FromUrl
  76. if (paramInfos.Count(x => x.FromType == FromType.FromUrl) > 1)
  77. {
  78. throw new InvalidOperationException($"Method {method.Name} defined more than one [FromUrl] parameters.");
  79. }
  80. //More than one params' FormType is FromBody
  81. if (paramInfos.Count(x => x.FromType == FromType.FromBody) > 1)
  82. {
  83. throw new InvalidOperationException($"Method {method.Name} defined more than one [FromBody] parameters.");
  84. }
  85. //More than one normal params and one of params the FormType is FromUrl
  86. if (paramInfos.Any(x => x.FromType == FromType.FromUrl) && paramInfos.Any(x=>x.FromType == FromType.None))
  87. {
  88. throw new InvalidOperationException($"Only one [FromUrl] parameter can be define in Method {method.Name} without any other parameters.");
  89. }
  90. //Get Method can not contains FromBody
  91. if (methodWebApiType == WebApiType.Get && paramInfos.Any(x => x.FromType == FromType.FromBody))
  92. {
  93. throw new InvalidOperationException($"Get method {method.Name} can not contains [FromBody] parameter.");
  94. }
  95. //Generate the delegate by Emit.
  96. var dynamicMethod = new DynamicMethod("", null, new[] { typeof(object), typeof(object[]) }, GetType().Module);
  97. var il = dynamicMethod.GetILGenerator();
  98. //Put the first arg in stack which is this object..
  99. il.Emit(OpCodes.Ldarg_S, 0);
  100. //Cast the object to the real type.
  101. il.Emit(OpCodes.Castclass, GetType());
  102. //Put WebApiHttpContext
  103. il.Emit(OpCodes.Ldarg_S, 1);
  104. //Put an integer value in stack which is the index in object[]
  105. il.Emit(OpCodes.Ldc_I4_S, 0);
  106. //Get the reference of index which is the integer value from the object[].
  107. il.Emit(OpCodes.Ldelem_Ref);
  108. //Cast or unbox the reference.
  109. il.Emit(OpCodes.Castclass, typeof(WebApiHttpContext));
  110. //Put all args which is object[] in stack.
  111. for (var i = 0; i < paramInfos.Count; i++)
  112. {
  113. var parameterInfo = paramInfos[i];
  114. //Put the arg 1 which is object[] in stack.
  115. il.Emit(OpCodes.Ldarg_S, 1);
  116. //Put an integer value in stack which is the index in object[]
  117. il.Emit(OpCodes.Ldc_I4_S, i+1);
  118. //Get the reference of index which is the integer value from the object[].
  119. il.Emit(OpCodes.Ldelem_Ref);
  120. //Cast or unbox the reference.
  121. il.Emit(parameterInfo.ParameterType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, parameterInfo.ParameterType);
  122. }
  123. //Call the method.
  124. il.Emit(OpCodes.Call, method);
  125. //Exit the method.
  126. il.Emit(OpCodes.Ret);
  127. //Create the delegate by dynamic method.
  128. var action = (Action<object, object[]>)dynamicMethod.CreateDelegate(typeof(Action<object, object[]>));
  129. var callingMethod = new CallingMethod(methodName.ToLower(), methodWebApiType, paramInfos, this, new WebApiMethod(action));
  130. _callingMethods.Add(callingMethod.Name, callingMethod);
  131. }
  132. }
  133. /// <summary>
  134. /// Get the CallingMethod from the handler by name.
  135. /// </summary>
  136. /// <param name="name">The name of the CallingMethod, if is null, will use the parameters to find the method.</param>
  137. /// <returns>The CallingMethod, null if not exist.</returns>
  138. public CallingMethod GetCallingMethod(string name)
  139. {
  140. return !_callingMethods.ContainsKey(name) ? null : _callingMethods[name];
  141. }
  142. /// <summary>
  143. /// Get all calling method provided by this handler.
  144. /// </summary>
  145. /// <returns>All CallingMethods</returns>
  146. public CallingMethod[] GetCallingMethods()
  147. {
  148. return _callingMethods.Values.ToArray();
  149. }
  150. }
  151. }