ServiceController.cs 7.4 KB

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