private static Dictionary <string, List <FieldDescriptor> > ResolveRouteParameterDescriptors(string pattern, MessageDescriptor messageDescriptor) { var routePattern = RoutePatternFactory.Parse(pattern); var routeParameterDescriptors = new Dictionary <string, List <FieldDescriptor> >(StringComparer.Ordinal); foreach (var routeParameter in routePattern.Parameters) { if (!ServiceDescriptorHelpers.TryResolveDescriptors(messageDescriptor, routeParameter.Name, out var fieldDescriptors)) { throw new InvalidOperationException($"Couldn't find matching field for route parameter '{routeParameter.Name}' on {messageDescriptor.Name}."); } routeParameterDescriptors.Add(routeParameter.Name, fieldDescriptors); } return(routeParameterDescriptors); }
private async Task <IMessage> CreateMessage(HttpRequest request) { IMessage?requestMessage; if (_bodyDescriptor != null) { if (request.ContentType == null || !request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("Request content-type of application/json is required."); } if (!request.Body.CanSeek) { // JsonParser does synchronous reads. In order to avoid blocking on the stream, we asynchronously // read everything into a buffer, and then seek back to the beginning. request.EnableBuffering(); Debug.Assert(request.Body.CanSeek); await request.Body.DrainAsync(CancellationToken.None); request.Body.Seek(0L, SeekOrigin.Begin); } var encoding = RequestEncoding.SelectCharacterEncoding(request); // TODO: Handle unsupported encoding IMessage bodyContent; using (var requestReader = new HttpRequestStreamReader(request.Body, encoding)) { bodyContent = JsonParser.Default.Parse(requestReader, _bodyDescriptor); } if (_bodyFieldDescriptor != null) { requestMessage = (IMessage)Activator.CreateInstance <TRequest>(); _bodyFieldDescriptor.Accessor.SetValue(requestMessage, bodyContent); } else { requestMessage = bodyContent; } } else { requestMessage = (IMessage)Activator.CreateInstance <TRequest>(); } foreach (var parameterDescriptor in _routeParameterDescriptors) { var routeValue = request.RouteValues[parameterDescriptor.Key]; if (routeValue != null) { ServiceDescriptorHelpers.RecursiveSetValue(requestMessage, parameterDescriptor.Value, routeValue); } } foreach (var item in request.Query) { if (CanBindQueryStringVariable(item.Key)) { if (!_queryParameterDescriptors.TryGetValue(item.Key, out var pathDescriptors)) { if (ServiceDescriptorHelpers.TryResolveDescriptors(requestMessage.Descriptor, item.Key, out pathDescriptors)) { _queryParameterDescriptors[item.Key] = pathDescriptors; } } if (pathDescriptors != null) { object value = item.Value.Count == 1 ? (object)item.Value[0] : item.Value; ServiceDescriptorHelpers.RecursiveSetValue(requestMessage, pathDescriptors, value); } } } return(requestMessage); }
private void AddMethodCore <TRequest, TResponse>( Method <TRequest, TResponse> method, 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); var methodContext = global::Grpc.Shared.Server.MethodOptions.Create(new[] { _globalOptions, _serviceOptions }); var routeParameterDescriptors = ResolveRouteParameterDescriptors(pattern, methodDescriptor.InputType); MessageDescriptor? bodyDescriptor = null; List <FieldDescriptor>?bodyFieldDescriptors = null; var bodyDescriptorRepeated = false; if (!string.IsNullOrEmpty(body)) { if (!string.Equals(body, "*", StringComparison.Ordinal)) { if (!ServiceDescriptorHelpers.TryResolveDescriptors(methodDescriptor.InputType, body, out bodyFieldDescriptors)) { throw new InvalidOperationException($"Couldn't find matching field for body '{body}' on {methodDescriptor.InputType.Name}."); } var leafDescriptor = bodyFieldDescriptors.Last(); if (leafDescriptor.IsRepeated) { // A repeating field isn't a message type. The JSON parser will parse using the containing // type to get the repeating collection. bodyDescriptor = leafDescriptor.ContainingType; bodyDescriptorRepeated = true; } else { bodyDescriptor = leafDescriptor.MessageType; } } else { bodyDescriptor = methodDescriptor.InputType; } } 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, RoutePatternFactory.Parse(pattern), metadata, unaryServerCallHandler.HandleCallAsync); } catch (Exception ex) { throw new InvalidOperationException($"Error binding {method.Name} on {typeof(TService).Name} to HTTP API.", ex); } }
private async Task <IMessage> CreateMessage(HttpContext httpContext) { IMessage?requestMessage; if (_bodyDescriptor != null) { if (string.Equals(httpContext.Request.ContentType, "application/json", StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("Request content-type of application/json is required."); } var ms = new MemoryStream(); await httpContext.Request.Body.CopyToAsync(ms); ms.Seek(0, SeekOrigin.Begin); var bodyContent = JsonParser.Default.Parse(new StreamReader(ms), _bodyDescriptor); if (_bodyFieldDescriptor != null) { requestMessage = (IMessage)Activator.CreateInstance <TRequest>(); _bodyFieldDescriptor.Accessor.SetValue(requestMessage, bodyContent); } else { requestMessage = bodyContent; } } else { requestMessage = (IMessage)Activator.CreateInstance <TRequest>(); } foreach (var parameterDescriptor in _routeParameterDescriptors) { var routeValue = httpContext.Request.RouteValues[parameterDescriptor.Key]; if (routeValue != null) { ServiceDescriptorHelpers.RecursiveSetValue(requestMessage, parameterDescriptor.Value, routeValue); } } foreach (var item in httpContext.Request.Query) { if (CanBindQueryStringVariable(item.Key)) { if (!_queryParameterDescriptors.TryGetValue(item.Key, out var pathDescriptors)) { if (ServiceDescriptorHelpers.TryResolveDescriptors(requestMessage.Descriptor, item.Key, out pathDescriptors)) { _queryParameterDescriptors[item.Key] = pathDescriptors; } } if (pathDescriptors != null) { object value = item.Value.Count == 1 ? (object)item.Value[0] : item.Value; ServiceDescriptorHelpers.RecursiveSetValue(requestMessage, pathDescriptors, value); } } } return(requestMessage); }