ServiceController.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reflection;
  4. using System.Threading.Tasks;
  5. using Emby.Server.Implementations.HttpServer;
  6. using MediaBrowser.Model.Logging;
  7. using MediaBrowser.Model.Services;
  8. namespace Emby.Server.Implementations.Services
  9. {
  10. public delegate Task<object> InstanceExecFn(IRequest requestContext, object intance, object request);
  11. public delegate object ActionInvokerFn(object intance, object request);
  12. public delegate void VoidActionInvokerFn(object intance, object request);
  13. public class ServiceController
  14. {
  15. public static ServiceController Instance;
  16. private readonly Func<IEnumerable<Type>> _resolveServicesFn;
  17. public ServiceController(Func<IEnumerable<Type>> resolveServicesFn)
  18. {
  19. Instance = this;
  20. _resolveServicesFn = resolveServicesFn;
  21. }
  22. public void Init(HttpListenerHost appHost)
  23. {
  24. foreach (var serviceType in _resolveServicesFn())
  25. {
  26. RegisterService(appHost, serviceType);
  27. }
  28. }
  29. private Type[] GetGenericArguments(Type type)
  30. {
  31. return type.GetTypeInfo().IsGenericTypeDefinition
  32. ? type.GetTypeInfo().GenericTypeParameters
  33. : type.GetTypeInfo().GenericTypeArguments;
  34. }
  35. public void RegisterService(HttpListenerHost appHost, Type serviceType)
  36. {
  37. var processedReqs = new HashSet<Type>();
  38. var actions = ServiceExecGeneral.Reset(serviceType);
  39. foreach (var mi in serviceType.GetActions())
  40. {
  41. var requestType = mi.GetParameters()[0].ParameterType;
  42. if (processedReqs.Contains(requestType)) continue;
  43. processedReqs.Add(requestType);
  44. ServiceExecGeneral.CreateServiceRunnersFor(requestType, actions);
  45. var returnMarker = GetTypeWithGenericTypeDefinitionOf(requestType, typeof(IReturn<>));
  46. var responseType = returnMarker != null ?
  47. GetGenericArguments(returnMarker)[0]
  48. : mi.ReturnType != typeof(object) && mi.ReturnType != typeof(void) ?
  49. mi.ReturnType
  50. : Type.GetType(requestType.FullName + "Response");
  51. RegisterRestPaths(appHost, requestType);
  52. appHost.AddServiceInfo(serviceType, requestType, responseType);
  53. }
  54. }
  55. private static Type GetTypeWithGenericTypeDefinitionOf(Type type, Type genericTypeDefinition)
  56. {
  57. foreach (var t in type.GetTypeInfo().ImplementedInterfaces)
  58. {
  59. if (t.GetTypeInfo().IsGenericType && t.GetGenericTypeDefinition() == genericTypeDefinition)
  60. {
  61. return t;
  62. }
  63. }
  64. var genericType = FirstGenericType(type);
  65. if (genericType != null && genericType.GetGenericTypeDefinition() == genericTypeDefinition)
  66. {
  67. return genericType;
  68. }
  69. return null;
  70. }
  71. public static Type FirstGenericType(Type type)
  72. {
  73. while (type != null)
  74. {
  75. if (type.GetTypeInfo().IsGenericType)
  76. return type;
  77. type = type.GetTypeInfo().BaseType;
  78. }
  79. return null;
  80. }
  81. public readonly Dictionary<string, List<RestPath>> RestPathMap = new Dictionary<string, List<RestPath>>(StringComparer.OrdinalIgnoreCase);
  82. public void RegisterRestPaths(HttpListenerHost appHost, Type requestType)
  83. {
  84. var attrs = appHost.GetRouteAttributes(requestType);
  85. foreach (RouteAttribute attr in attrs)
  86. {
  87. var restPath = new RestPath(appHost.CreateInstance, appHost.GetParseFn, requestType, attr.Path, attr.Verbs, attr.Summary, attr.Notes);
  88. if (!restPath.IsValid)
  89. throw new NotSupportedException(string.Format(
  90. "RestPath '{0}' on Type '{1}' is not Valid", attr.Path, requestType.GetMethodName()));
  91. RegisterRestPath(restPath);
  92. }
  93. }
  94. private static readonly char[] InvalidRouteChars = new[] { '?', '&' };
  95. public void RegisterRestPath(RestPath restPath)
  96. {
  97. if (!restPath.Path.StartsWith("/"))
  98. throw new ArgumentException(string.Format("Route '{0}' on '{1}' must start with a '/'", restPath.Path, restPath.RequestType.GetMethodName()));
  99. if (restPath.Path.IndexOfAny(InvalidRouteChars) != -1)
  100. throw new ArgumentException(string.Format("Route '{0}' on '{1}' contains invalid chars. " +
  101. "See https://github.com/ServiceStack/ServiceStack/wiki/Routing for info on valid routes.", restPath.Path, restPath.RequestType.GetMethodName()));
  102. List<RestPath> pathsAtFirstMatch;
  103. if (!RestPathMap.TryGetValue(restPath.FirstMatchHashKey, out pathsAtFirstMatch))
  104. {
  105. pathsAtFirstMatch = new List<RestPath>();
  106. RestPathMap[restPath.FirstMatchHashKey] = pathsAtFirstMatch;
  107. }
  108. pathsAtFirstMatch.Add(restPath);
  109. }
  110. public RestPath GetRestPathForRequest(string httpMethod, string pathInfo, ILogger logger)
  111. {
  112. var matchUsingPathParts = RestPath.GetPathPartsForMatching(pathInfo);
  113. List<RestPath> firstMatches;
  114. var yieldedHashMatches = RestPath.GetFirstMatchHashKeys(matchUsingPathParts);
  115. foreach (var potentialHashMatch in yieldedHashMatches)
  116. {
  117. if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches))
  118. {
  119. continue;
  120. }
  121. var bestScore = -1;
  122. foreach (var restPath in firstMatches)
  123. {
  124. var score = restPath.MatchScore(httpMethod, matchUsingPathParts, logger);
  125. if (score > bestScore) bestScore = score;
  126. }
  127. if (bestScore > 0)
  128. {
  129. foreach (var restPath in firstMatches)
  130. {
  131. if (bestScore == restPath.MatchScore(httpMethod, matchUsingPathParts, logger))
  132. return restPath;
  133. }
  134. }
  135. }
  136. var yieldedWildcardMatches = RestPath.GetFirstMatchWildCardHashKeys(matchUsingPathParts);
  137. foreach (var potentialHashMatch in yieldedWildcardMatches)
  138. {
  139. if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches)) continue;
  140. var bestScore = -1;
  141. foreach (var restPath in firstMatches)
  142. {
  143. var score = restPath.MatchScore(httpMethod, matchUsingPathParts, logger);
  144. if (score > bestScore) bestScore = score;
  145. }
  146. if (bestScore > 0)
  147. {
  148. foreach (var restPath in firstMatches)
  149. {
  150. if (bestScore == restPath.MatchScore(httpMethod, matchUsingPathParts, logger))
  151. return restPath;
  152. }
  153. }
  154. }
  155. return null;
  156. }
  157. public async Task<object> Execute(HttpListenerHost appHost, object requestDto, IRequest req)
  158. {
  159. req.Dto = requestDto;
  160. var requestType = requestDto.GetType();
  161. req.OperationName = requestType.Name;
  162. var serviceType = appHost.GetServiceTypeByRequest(requestType);
  163. var service = appHost.CreateInstance(serviceType);
  164. //var service = typeFactory.CreateInstance(serviceType);
  165. var serviceRequiresContext = service as IRequiresRequest;
  166. if (serviceRequiresContext != null)
  167. {
  168. serviceRequiresContext.Request = req;
  169. }
  170. if (req.Dto == null) // Don't override existing batched DTO[]
  171. req.Dto = requestDto;
  172. //Executes the service and returns the result
  173. var response = await ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName()).ConfigureAwait(false);
  174. return response;
  175. }
  176. }
  177. }