/// <summary>Processes the HTTP request body binding.</summary> /// <param name="context">The context.</param> /// <param name="contextResolver">The API request context resolver.</param> /// <param name="formatterFactory">The formatter factory.</param> /// <returns></returns> internal static async Task <bool> ProcessHttpRequestBodyBinding(this ApiRequestContext context, IApiRequestContextResolver contextResolver, IDeepSleepMediaSerializerFactory formatterFactory) { if (!context.RequestAborted.IsCancellationRequested) { if (context.Request.Method?.In(StringComparison.InvariantCultureIgnoreCase, "post", "patch", "put") ?? false) { if (!context.Request.ContentLength.HasValue) { if (context.Configuration?.RequestValidation?.RequireContentLengthOnRequestBodyRequests ?? true) { context.Response.StatusCode = 411; return(false); } } if (context.Request.ContentLength > 0 && string.IsNullOrWhiteSpace(context.Request.ContentType)) { context.Response.StatusCode = 415; return(false); } if (context.Configuration?.RequestValidation?.MaxRequestLength > 0 && context.Request.ContentLength > 0) { if (context.Request.ContentLength > context.Configuration.RequestValidation.MaxRequestLength) { context.Response.StatusCode = 413; return(false); } } if (context.Request.ContentLength > 0 && context.Routing?.Route?.Location?.BodyParameterType == null) { if (!(context.Configuration?.RequestValidation?.AllowRequestBodyWhenNoModelDefined ?? false)) { context.Response.StatusCode = 413; return(false); } } if (context.Routing.Route.Location.BodyParameterType != null && context.Request.ContentLength > 0 && !string.IsNullOrWhiteSpace(context.Request.ContentType)) { IDeepSleepMediaSerializer formatter = null; var formatterTypes = context.Configuration?.ReadWriteConfiguration?.ReadableMediaTypes ?? formatterFactory?.GetReadableTypes(objType: context.Routing.Route.Location.BodyParameterType, overridingFormatters: null) ?? new List <string>(); if (formatterFactory != null) { formatter = await formatterFactory.GetContentTypeFormatter( contentTypeHeader : context.Request.ContentType, objType : context.Routing.Route.Location.BodyParameterType, formatterType : out var _, readableMediaTypes : context.Configuration?.ReadWriteConfiguration?.ReadableMediaTypes).ConfigureAwait(false); } if (context.Configuration.ReadWriteConfiguration?.ReaderResolver != null) { var overrides = await context.Configuration.ReadWriteConfiguration.ReaderResolver(context?.RequestServices).ConfigureAwait(false); if (overrides?.Formatters != null) { formatter = await formatterFactory.GetContentTypeFormatter( contentTypeHeader : context.Request.ContentType, objType : context.Routing.Route.Location.BodyParameterType, formatterType : out var _, readableFormatters : overrides.Formatters, readableMediaTypes : context.Configuration?.ReadWriteConfiguration?.ReadableMediaTypes).ConfigureAwait(false); formatterTypes = overrides.Formatters .Where(f => f != null) .Where(f => f.SupportsRead) .Where(f => f.ReadableMediaTypes != null) .Where(f => f.CanHandleType(context.Routing.Route.Location.BodyParameterType)) .SelectMany(f => f.ReadableMediaTypes) .Distinct() .ToList(); formatterTypes = context.Configuration?.ReadWriteConfiguration?.ReadableMediaTypes ?? formatterTypes ?? new List <string>(); } else { formatterTypes = context.Configuration?.ReadWriteConfiguration?.ReadableMediaTypes ?? new List <string>(); } } if (formatter == null) { context.Response.StatusCode = 415; context.Response.AddHeader( name: "X-Allow-Content-Types", value: string.Join(", ", formatterTypes), append: false, allowMultiple: false); return(false); } Encoding contextTypeEncoding = null; if (context.Request.ContentType != null as string) { if (!string.IsNullOrWhiteSpace(context.Request.ContentType.Charset)) { try { contextTypeEncoding = Encoding.GetEncoding(context.Request.ContentType.Charset); } catch { } } } try { context.Request.InvocationContext.BodyModel = await formatter.ReadType( stream : context.Request.Body, objType : context.Routing.Route.Location.BodyParameterType, options : new MediaSerializerOptions { Culture = context.Request.AcceptCulture, Encoding = contextTypeEncoding ?? Encoding.UTF8 }).ConfigureAwait(false); } catch (Exception ex) { context.AddInternalException(ex); if (ex.GetType().Name.Contains("BadHttpRequestException")) { context.Response.StatusCode = 413; } else { context.AddValidationError(context.Configuration?.ValidationErrorConfiguration?.RequestDeserializationError); context.Response.StatusCode = 400; } return(false); } } } return(true); } return(false); }
/// <summary>Processes the HTTP endpoint invocation.</summary> /// <param name="context">The context.</param> /// <returns></returns> internal static async Task <bool> ProcessHttpEndpointInvocation(this ApiRequestContext context) { if (!context.RequestAborted.IsCancellationRequested) { if (context.Routing?.Route?.Location?.MethodInfo != null) { var parameters = new List <object>(); bool addedUriParam = false; bool addedBodyParam = false; // ----------------------------------------------------------- // Build the parameters list to invoke the controller method // This includes the UriModel and BodyModel if they exist. // If any other parameters exists on the controller method // they'll be passed a null value. A possible enhancement // would be to pull the extra parameters from the DI container // ----------------------------------------------------------- foreach (var param in context.Routing.Route.Location.MethodInfo.GetParameters()) { if (!addedUriParam && context.Request.InvocationContext.UriModel != null && param.GetCustomAttribute <InUriAttribute>() != null) { parameters.Add(context.Request.InvocationContext.UriModel); addedUriParam = true; } else if (!addedBodyParam && context.Request.InvocationContext.BodyModel != null && param.GetCustomAttribute <InBodyAttribute>() != null) { parameters.Add(context.Request.InvocationContext.BodyModel); addedBodyParam = true; } else { var simpleParameter = context.Request.InvocationContext.SimpleParameters .Where(p => p.Key.Name == param.Name && p.Key.ParameterType == param.ParameterType) .FirstOrDefault(); if (simpleParameter.Value != null) { parameters.Add(simpleParameter.Value); } else { parameters.Add(param.ParameterType.GetDefaultValue()); } } } // ----------------------------------------------------- // Invoke the controller method with the parameters list // ----------------------------------------------------- object endpointResponse; try { endpointResponse = context.Routing.Route.Location.MethodInfo.Invoke( context.Request.InvocationContext.ControllerInstance, parameters.ToArray()); } catch (TargetInvocationException ex) { if (ex.InnerException != null) { throw ex.InnerException; } else { throw; } } // ----------------------------------------------------- // If the response is awaitable then handle // the await on the result // ----------------------------------------------------- if (endpointResponse as Task != null) { await((Task)endpointResponse).ConfigureAwait(false); var resultProperty = endpointResponse.GetType().GetProperty("Result"); var response = resultProperty?.GetValue(endpointResponse); if (response != null && response.GetType().FullName != "System.Threading.Tasks.VoidTaskResult") { endpointResponse = response; } else { endpointResponse = null; } } if (endpointResponse as IApiResponse != null) { context.Response.ResponseObject = ((IApiResponse)endpointResponse).Response; context.Response.StatusCode = ((IApiResponse)endpointResponse).StatusCode; context.Runtime.Internals.IsOverridingStatusCode = true; var headers = ((IApiResponse)endpointResponse).Headers; if (headers != null) { headers .Where(h => h != null) .ToList() .ForEach(h => context.Response.AddHeader(h.Name, h.Value)); } if (endpointResponse as IApiErrorResponse != null) { var errors = ((IApiErrorResponse)endpointResponse).Errors; if (errors != null) { errors.ForEach(e => context.AddValidationError(e)); } } } else { context.Response.ResponseObject = endpointResponse; } } return(true); } return(false); }