private void AddMethodCore <TRequest, TResponse>( Method <TRequest, TResponse> method, HttpRule httpRule, string pattern, string httpVerb, string body, string responseBody, MethodDescriptor methodDescriptor) where TRequest : class where TResponse : class { try { if (!pattern.StartsWith('/')) { // This validation is consistent with grpc-gateway code generation. // We should match their validation to be a good member of the eco-system. throw new InvalidOperationException($"Path template must start with /: {pattern}"); } var(invoker, metadata) = CreateModelCore <UnaryServerMethod <TService, TRequest, TResponse> >( method.Name, new[] { typeof(TRequest), typeof(ServerCallContext) }, httpVerb, httpRule, methodDescriptor); var methodContext = global::Grpc.Shared.Server.MethodOptions.Create(new[] { _globalOptions, _serviceOptions }); var routePattern = RoutePatternFactory.Parse(pattern); var routeParameterDescriptors = ServiceDescriptorHelpers.ResolveRouteParameterDescriptors(routePattern, methodDescriptor.InputType); ServiceDescriptorHelpers.ResolveBodyDescriptor(body, methodDescriptor, out var bodyDescriptor, out var bodyFieldDescriptors, out var bodyDescriptorRepeated); FieldDescriptor?responseBodyDescriptor = null; if (!string.IsNullOrEmpty(responseBody)) { responseBodyDescriptor = methodDescriptor.OutputType.FindFieldByName(responseBody); if (responseBodyDescriptor == null) { throw new InvalidOperationException($"Couldn't find matching field for response body '{responseBody}' on {methodDescriptor.OutputType.Name}."); } } var unaryInvoker = new UnaryServerMethodInvoker <TService, TRequest, TResponse>(invoker, method, methodContext, _serviceActivator); var unaryServerCallHandler = new UnaryServerCallHandler <TService, TRequest, TResponse>( unaryInvoker, responseBodyDescriptor, bodyDescriptor, bodyDescriptorRepeated, bodyFieldDescriptors, routeParameterDescriptors); _context.AddMethod <TRequest, TResponse>(method, routePattern, metadata, unaryServerCallHandler.HandleCallAsync); } catch (Exception ex) { throw new InvalidOperationException($"Error binding {method.Name} on {typeof(TService).Name} to HTTP API.", ex); } }
private ApiDescriptionGroupCollection GetCollection() { var descriptions = new List <ApiDescription>(); var endpoints = _endpointDataSource.Endpoints; foreach (var endpoint in endpoints) { if (endpoint is RouteEndpoint routeEndpoint) { var grpcMetadata = endpoint.Metadata.GetMetadata <GrpcHttpMetadata>(); if (grpcMetadata != null) { var httpRule = grpcMetadata.HttpRule; var methodDescriptor = grpcMetadata.MethodDescriptor; if (ServiceDescriptorHelpers.TryResolvePattern(grpcMetadata.HttpRule, out var pattern, out var verb)) { var apiDescription = new ApiDescription(); apiDescription.HttpMethod = verb; apiDescription.ActionDescriptor = new ActionDescriptor { RouteValues = new Dictionary <string, string> { // Swagger uses this to group endpoints together. // Group methods together using the service name. ["controller"] = methodDescriptor.Service.FullName } }; apiDescription.RelativePath = pattern.TrimStart('/'); apiDescription.SupportedRequestFormats.Add(new ApiRequestFormat { MediaType = "application/json" }); apiDescription.SupportedResponseTypes.Add(new ApiResponseType { ApiResponseFormats = { new ApiResponseFormat { MediaType = "application/json" } }, ModelMetadata = new GrpcModelMetadata(ModelMetadataIdentity.ForType(methodDescriptor.OutputType.ClrType)), StatusCode = 200 }); var routeParameters = ServiceDescriptorHelpers.ResolveRouteParameterDescriptors(routeEndpoint.RoutePattern, methodDescriptor.InputType); foreach (var routeParameter in routeParameters) { var field = routeParameter.Value.Last(); apiDescription.ParameterDescriptions.Add(new ApiParameterDescription { Name = routeParameter.Key, ModelMetadata = new GrpcModelMetadata(ModelMetadataIdentity.ForType(MessageDescriptorHelpers.ResolveFieldType(field))), Source = BindingSource.Path, DefaultValue = string.Empty }); } ServiceDescriptorHelpers.ResolveBodyDescriptor(httpRule.Body, methodDescriptor, out var bodyDescriptor, out _, out _); if (bodyDescriptor != null) { apiDescription.ParameterDescriptions.Add(new ApiParameterDescription { Name = "Input", ModelMetadata = new GrpcModelMetadata(ModelMetadataIdentity.ForType(bodyDescriptor.ClrType)), Source = BindingSource.Body }); } descriptions.Add(apiDescription); } } } } var groups = new List <ApiDescriptionGroup>(); groups.Add(new ApiDescriptionGroup("Grpc", descriptions)); return(new ApiDescriptionGroupCollection(groups, 1)); }