ProviderServiceBinder.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. using Grpc.AspNetCore.Server.Model;
  2. using Grpc.Core;
  3. using Grpc.Extension.Abstract;
  4. using Grpc.Extension.BaseService;
  5. using Grpc.Extension.BaseService.Model;
  6. using Microsoft.AspNetCore.Routing;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Reflection;
  10. using System.Text;
  11. namespace Grpc.Extension.AspNetCore.Internal
  12. {
  13. internal class ProviderServiceBinder<TService> : ServiceBinderBase where TService : class
  14. {
  15. private readonly ServiceMethodProviderContext<TService> _context;
  16. private readonly Type _declaringType;
  17. private readonly bool _isIGrpcService;
  18. private readonly bool _isIGrpcBaseService;
  19. internal ProviderServiceBinder(ServiceMethodProviderContext<TService> context, Type declaringType)
  20. {
  21. _context = context;
  22. _declaringType = declaringType;
  23. _isIGrpcService = typeof(IGrpcService).IsAssignableFrom(typeof(TService));
  24. _isIGrpcBaseService = typeof(IGrpcBaseService).IsAssignableFrom(typeof(TService));
  25. }
  26. public override void AddMethod<TRequest, TResponse>(Method<TRequest, TResponse> method, ClientStreamingServerMethod<TRequest, TResponse> handler)
  27. {
  28. var (invoker, metadata) = CreateModelCore<ClientStreamingServerMethod<TService, TRequest, TResponse>>(
  29. method.Name,
  30. new[] { typeof(IAsyncStreamReader<TRequest>), typeof(ServerCallContext) });
  31. if (_isIGrpcService)
  32. {
  33. _context.AddClientStreamingMethod<TRequest, TResponse>(method, metadata, invoker);
  34. }
  35. AddMetaMethod((new MetaMethodModel
  36. {
  37. FullName = method.FullName,
  38. RequestType = typeof(TRequest),
  39. ResponseType = typeof(TResponse),
  40. ServiceType = typeof(TService),
  41. Handler = invoker
  42. }));
  43. }
  44. public override void AddMethod<TRequest, TResponse>(Method<TRequest, TResponse> method, DuplexStreamingServerMethod<TRequest, TResponse> handler)
  45. {
  46. var (invoker, metadata) = CreateModelCore<DuplexStreamingServerMethod<TService, TRequest, TResponse>>(
  47. method.Name,
  48. new[] { typeof(IAsyncStreamReader<TRequest>), typeof(IServerStreamWriter<TResponse>), typeof(ServerCallContext) });
  49. if (_isIGrpcService)
  50. {
  51. _context.AddDuplexStreamingMethod<TRequest, TResponse>(method, metadata, invoker);
  52. }
  53. AddMetaMethod((new MetaMethodModel
  54. {
  55. FullName = method.FullName,
  56. RequestType = typeof(TRequest),
  57. ResponseType = typeof(TResponse),
  58. ServiceType = typeof(TService),
  59. Handler = invoker
  60. }));
  61. }
  62. public override void AddMethod<TRequest, TResponse>(Method<TRequest, TResponse> method, ServerStreamingServerMethod<TRequest, TResponse> handler)
  63. {
  64. var (invoker, metadata) = CreateModelCore<ServerStreamingServerMethod<TService, TRequest, TResponse>>(
  65. method.Name,
  66. new[] { typeof(TRequest), typeof(IServerStreamWriter<TResponse>), typeof(ServerCallContext) });
  67. if (_isIGrpcService)
  68. {
  69. _context.AddServerStreamingMethod<TRequest, TResponse>(method, metadata, invoker);
  70. }
  71. AddMetaMethod((new MetaMethodModel
  72. {
  73. FullName = method.FullName,
  74. RequestType = typeof(TRequest),
  75. ResponseType = typeof(TResponse),
  76. ServiceType = typeof(TService),
  77. Handler = invoker
  78. }));
  79. }
  80. public override void AddMethod<TRequest, TResponse>(Method<TRequest, TResponse> method, UnaryServerMethod<TRequest, TResponse> handler)
  81. {
  82. var (invoker, metadata) = CreateModelCore<UnaryServerMethod<TService, TRequest, TResponse>>(
  83. method.Name,
  84. new[] { typeof(TRequest), typeof(ServerCallContext) });
  85. if (_isIGrpcService)
  86. {
  87. _context.AddUnaryMethod<TRequest, TResponse>(method, metadata, invoker);
  88. }
  89. AddMetaMethod((new MetaMethodModel
  90. {
  91. FullName = method.FullName,
  92. RequestType = typeof(TRequest),
  93. ResponseType = typeof(TResponse),
  94. ServiceType = typeof(TService),
  95. Handler = invoker
  96. }));
  97. }
  98. private Delegate GetOrCreateHandler<TDelegate>(Delegate handler ,string methodName, Type[] methodParameters)
  99. {
  100. if (handler != null) return handler;
  101. //创建Handler
  102. var handlerMethod = GetMethod(methodName, methodParameters);
  103. if (handlerMethod == null)
  104. {
  105. throw new InvalidOperationException($"Could not find '{methodName}' on {typeof(TService)}.");
  106. }
  107. handler = handlerMethod.CreateDelegate(typeof(TDelegate),null);
  108. return handler;
  109. }
  110. private (TDelegate invoker, List<object> metadata) CreateModelCore<TDelegate>(string methodName, Type[] methodParameters) where TDelegate : Delegate
  111. {
  112. var handlerMethod = GetMethod(methodName, methodParameters);
  113. if (handlerMethod == null)
  114. {
  115. throw new InvalidOperationException($"Could not find '{methodName}' on {typeof(TService)}.");
  116. }
  117. var invoker = (TDelegate)Delegate.CreateDelegate(typeof(TDelegate), handlerMethod);
  118. var metadata = new List<object>();
  119. // Add type metadata first so it has a lower priority
  120. metadata.AddRange(typeof(TService).GetCustomAttributes(inherit: true));
  121. // Add method metadata last so it has a higher priority
  122. metadata.AddRange(handlerMethod.GetCustomAttributes(inherit: true));
  123. // Accepting CORS preflight means gRPC will allow requests with OPTIONS + preflight headers.
  124. // If CORS middleware hasn't been configured then the request will reach gRPC handler.
  125. // gRPC will return 405 response and log that CORS has not been configured.
  126. metadata.Add(new HttpMethodMetadata(new[] { "POST" }, acceptCorsPreflight: true));
  127. return (invoker, metadata);
  128. }
  129. private MethodInfo? GetMethod(string methodName, Type[] methodParameters)
  130. {
  131. Type? currentType = typeof(TService);
  132. while (currentType != null)
  133. {
  134. var matchingMethod = currentType.GetMethod(
  135. methodName,
  136. BindingFlags.Public | BindingFlags.Instance,
  137. binder: null,
  138. types: methodParameters,
  139. modifiers: null);
  140. if (matchingMethod == null)
  141. {
  142. return null;
  143. }
  144. if (_isIGrpcService)
  145. {
  146. return matchingMethod;
  147. }
  148. // Validate that the method overrides the virtual method on the base service type.
  149. // If there is a method with the same name it will hide the base method. Ignore it,
  150. // and continue searching on the base type.
  151. if (matchingMethod.IsVirtual)
  152. {
  153. var baseDefinitionMethod = matchingMethod.GetBaseDefinition();
  154. if (baseDefinitionMethod != null && baseDefinitionMethod.DeclaringType == _declaringType)
  155. {
  156. return matchingMethod;
  157. }
  158. }
  159. currentType = currentType.BaseType;
  160. }
  161. return null;
  162. }
  163. private void AddMetaMethod(MetaMethodModel model)
  164. {
  165. if (_isIGrpcBaseService) return;
  166. MetaModel.Methods.Add(model);
  167. }
  168. }
  169. }