ServiceController.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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 Microsoft.Extensions.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. public ServiceController()
  17. {
  18. Instance = this;
  19. }
  20. public void Init(HttpListenerHost appHost, Type[] serviceTypes)
  21. {
  22. foreach (var serviceType in serviceTypes)
  23. {
  24. RegisterService(appHost, serviceType);
  25. }
  26. }
  27. public void RegisterService(HttpListenerHost appHost, Type serviceType)
  28. {
  29. var processedReqs = new HashSet<Type>();
  30. var actions = ServiceExecGeneral.Reset(serviceType);
  31. foreach (var mi in serviceType.GetActions())
  32. {
  33. var requestType = mi.GetParameters()[0].ParameterType;
  34. if (processedReqs.Contains(requestType)) continue;
  35. processedReqs.Add(requestType);
  36. ServiceExecGeneral.CreateServiceRunnersFor(requestType, actions);
  37. //var returnMarker = GetTypeWithGenericTypeDefinitionOf(requestType, typeof(IReturn<>));
  38. //var responseType = returnMarker != null ?
  39. // GetGenericArguments(returnMarker)[0]
  40. // : mi.ReturnType != typeof(object) && mi.ReturnType != typeof(void) ?
  41. // mi.ReturnType
  42. // : Type.GetType(requestType.FullName + "Response");
  43. RegisterRestPaths(appHost, requestType, serviceType);
  44. appHost.AddServiceInfo(serviceType, requestType);
  45. }
  46. }
  47. public static Type FirstGenericType(Type type)
  48. {
  49. while (type != null)
  50. {
  51. if (type.GetTypeInfo().IsGenericType)
  52. return type;
  53. type = type.GetTypeInfo().BaseType;
  54. }
  55. return null;
  56. }
  57. public readonly RestPath.RestPathMap RestPathMap = new RestPath.RestPathMap();
  58. public void RegisterRestPaths(HttpListenerHost appHost, Type requestType, Type serviceType)
  59. {
  60. var attrs = appHost.GetRouteAttributes(requestType);
  61. foreach (RouteAttribute attr in attrs)
  62. {
  63. var restPath = new RestPath(appHost.CreateInstance, appHost.GetParseFn, requestType, serviceType, attr.Path, attr.Verbs, attr.IsHidden, attr.Summary, attr.Description);
  64. RegisterRestPath(restPath);
  65. }
  66. }
  67. private static readonly char[] InvalidRouteChars = new[] { '?', '&' };
  68. public void RegisterRestPath(RestPath restPath)
  69. {
  70. if (!restPath.Path.StartsWith("/"))
  71. throw new ArgumentException(string.Format("Route '{0}' on '{1}' must start with a '/'", restPath.Path, restPath.RequestType.GetMethodName()));
  72. if (restPath.Path.IndexOfAny(InvalidRouteChars) != -1)
  73. throw new ArgumentException(string.Format("Route '{0}' on '{1}' contains invalid chars. ", restPath.Path, restPath.RequestType.GetMethodName()));
  74. List<RestPath> pathsAtFirstMatch;
  75. if (!RestPathMap.TryGetValue(restPath.FirstMatchHashKey, out pathsAtFirstMatch))
  76. {
  77. pathsAtFirstMatch = new List<RestPath>();
  78. RestPathMap[restPath.FirstMatchHashKey] = pathsAtFirstMatch;
  79. }
  80. pathsAtFirstMatch.Add(restPath);
  81. }
  82. public RestPath GetRestPathForRequest(string httpMethod, string pathInfo)
  83. {
  84. var matchUsingPathParts = RestPath.GetPathPartsForMatching(pathInfo);
  85. List<RestPath> firstMatches;
  86. var yieldedHashMatches = RestPath.GetFirstMatchHashKeys(matchUsingPathParts);
  87. foreach (var potentialHashMatch in yieldedHashMatches)
  88. {
  89. if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches))
  90. {
  91. continue;
  92. }
  93. var bestScore = -1;
  94. RestPath bestMatch = null;
  95. foreach (var restPath in firstMatches)
  96. {
  97. var score = restPath.MatchScore(httpMethod, matchUsingPathParts);
  98. if (score > bestScore)
  99. {
  100. bestScore = score;
  101. bestMatch = restPath;
  102. }
  103. }
  104. if (bestScore > 0 && bestMatch != null)
  105. {
  106. return bestMatch;
  107. }
  108. }
  109. var yieldedWildcardMatches = RestPath.GetFirstMatchWildCardHashKeys(matchUsingPathParts);
  110. foreach (var potentialHashMatch in yieldedWildcardMatches)
  111. {
  112. if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches)) continue;
  113. var bestScore = -1;
  114. RestPath bestMatch = null;
  115. foreach (var restPath in firstMatches)
  116. {
  117. var score = restPath.MatchScore(httpMethod, matchUsingPathParts);
  118. if (score > bestScore)
  119. {
  120. bestScore = score;
  121. bestMatch = restPath;
  122. }
  123. }
  124. if (bestScore > 0 && bestMatch != null)
  125. {
  126. return bestMatch;
  127. }
  128. }
  129. return null;
  130. }
  131. public Task<object> Execute(HttpListenerHost appHost, object requestDto, IRequest req)
  132. {
  133. req.Dto = requestDto;
  134. var requestType = requestDto.GetType();
  135. req.OperationName = requestType.Name;
  136. var serviceType = appHost.GetServiceTypeByRequest(requestType);
  137. var service = appHost.CreateInstance(serviceType);
  138. //var service = typeFactory.CreateInstance(serviceType);
  139. var serviceRequiresContext = service as IRequiresRequest;
  140. if (serviceRequiresContext != null)
  141. {
  142. serviceRequiresContext.Request = req;
  143. }
  144. if (req.Dto == null) // Don't override existing batched DTO[]
  145. req.Dto = requestDto;
  146. //Executes the service and returns the result
  147. return ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName());
  148. }
  149. }
  150. }