private ApiDescription CreateApiDescription( ControllerActionDescriptor action, string httpMethod, string groupName) { var parsedTemplate = ParseTemplate(action); var apiDescription = new ApiDescription() { ActionDescriptor = action, GroupName = groupName, HttpMethod = httpMethod, RelativePath = GetRelativePath(parsedTemplate), }; var templateParameters = parsedTemplate?.Parameters?.ToList() ?? new List <TemplatePart>(); var parameterContext = new ApiParameterContext(_modelMetadataProvider, action, templateParameters); foreach (var parameter in GetParameters(parameterContext)) { apiDescription.ParameterDescriptions.Add(parameter); } var requestMetadataAttributes = GetRequestMetadataAttributes(action); var responseMetadataAttributes = GetResponseMetadataAttributes(action); // We only provide response info if we can figure out a type that is a user-data type. // Void /Task object/IActionResult will result in no data. var declaredReturnType = GetDeclaredReturnType(action); var runtimeReturnType = GetRuntimeReturnType(declaredReturnType); var apiResponseTypes = GetApiResponseTypes(responseMetadataAttributes, runtimeReturnType); foreach (var apiResponseType in apiResponseTypes) { apiDescription.SupportedResponseTypes.Add(apiResponseType); } // It would be possible here to configure an action with multiple body parameters, in which case you // could end up with duplicate data. foreach (var parameter in apiDescription.ParameterDescriptions.Where(p => p.Source == BindingSource.Body)) { var requestFormats = GetRequestFormats(requestMetadataAttributes, parameter.Type); foreach (var format in requestFormats) { apiDescription.SupportedRequestFormats.Add(format); } } return(apiDescription); }
private ApiDescription CreateApiDescription( ControllerActionDescriptor action, string httpMethod, string groupName) { var parsedTemplate = ParseTemplate(action); var apiDescription = new ApiDescription() { ActionDescriptor = action, GroupName = groupName, HttpMethod = httpMethod, RelativePath = GetRelativePath(parsedTemplate), }; var templateParameters = parsedTemplate?.Parameters?.ToList() ?? new List <TemplatePart>(); var parameterContext = new ApiParameterContext(_modelMetadataProvider, action, templateParameters); foreach (var parameter in GetParameters(parameterContext)) { apiDescription.ParameterDescriptions.Add(parameter); } var requestMetadataAttributes = GetRequestMetadataAttributes(action); var responseMetadataAttributes = GetResponseMetadataAttributes(action); // We only provide response info if we can figure out a type that is a user-data type. // Void /Task object/IActionResult will result in no data. var declaredReturnType = GetDeclaredReturnType(action); var runtimeReturnType = GetRuntimeReturnType(declaredReturnType); var apiResponseTypes = GetApiResponseTypes(responseMetadataAttributes, runtimeReturnType); foreach (var apiResponseType in apiResponseTypes) { apiDescription.SupportedResponseTypes.Add(apiResponseType); } // It would be possible here to configure an action with multiple body parameters, in which case you // could end up with duplicate data. if (apiDescription.ParameterDescriptions.Count > 0) { var contentTypes = GetDeclaredContentTypes(requestMetadataAttributes); foreach (var parameter in apiDescription.ParameterDescriptions) { if (parameter.Source == BindingSource.Body) { // For request body bound parameters, determine the content types supported // by input formatters. var requestFormats = GetSupportedFormats(contentTypes, parameter.Type); foreach (var format in requestFormats) { apiDescription.SupportedRequestFormats.Add(format); } } else if (parameter.Source == BindingSource.FormFile) { // Add all declared media types since FormFiles do not get processed by formatters. foreach (var contentType in contentTypes) { apiDescription.SupportedRequestFormats.Add(new ApiRequestFormat { MediaType = contentType, }); } } } } return(apiDescription); }
internal PseudoModelBindingVisitor(ApiParameterContext context, ParameterDescriptor parameter) { Context = context; Parameter = parameter; }
private IList <ApiParameterDescription> GetParameters(ApiParameterContext context) { // First, get parameters from the model-binding/parameter-binding side of the world. if (context.ActionDescriptor.Parameters != null) { foreach (var actionParameter in context.ActionDescriptor.Parameters) { var visitor = new PseudoModelBindingVisitor(context, actionParameter); var metadata = _modelMetadataProvider.GetMetadataForType(actionParameter.ParameterType); var bindingContext = ApiParameterDescriptionContext.GetContext( metadata, actionParameter.BindingInfo, propertyName: actionParameter.Name); visitor.WalkParameter(bindingContext); } } if (context.ActionDescriptor.BoundProperties != null) { foreach (var actionParameter in context.ActionDescriptor.BoundProperties) { var visitor = new PseudoModelBindingVisitor(context, actionParameter); var modelMetadata = context.MetadataProvider.GetMetadataForProperty( containerType: context.ActionDescriptor.ControllerTypeInfo.AsType(), propertyName: actionParameter.Name); var bindingContext = ApiParameterDescriptionContext.GetContext( modelMetadata, actionParameter.BindingInfo, propertyName: actionParameter.Name); visitor.WalkParameter(bindingContext); } } for (var i = context.Results.Count - 1; i >= 0; i--) { // Remove any 'hidden' parameters. These are things that can't come from user input, // so they aren't worth showing. if (!context.Results[i].Source.IsFromRequest) { context.Results.RemoveAt(i); } } // Next, we want to join up any route parameters with those discovered from the action's parameters. var routeParameters = new Dictionary <string, ApiParameterRouteInfo>(StringComparer.OrdinalIgnoreCase); foreach (var routeParameter in context.RouteParameters) { routeParameters.Add(routeParameter.Name, CreateRouteInfo(routeParameter)); } foreach (var parameter in context.Results) { if (parameter.Source == BindingSource.Path || parameter.Source == BindingSource.ModelBinding || parameter.Source == BindingSource.Custom) { ApiParameterRouteInfo routeInfo; if (routeParameters.TryGetValue(parameter.Name, out routeInfo)) { parameter.RouteInfo = routeInfo; routeParameters.Remove(parameter.Name); if (parameter.Source == BindingSource.ModelBinding && !parameter.RouteInfo.IsOptional) { // If we didn't see any information about the parameter, but we have // a route parameter that matches, let's switch it to path. parameter.Source = BindingSource.Path; } } } } // Lastly, create a parameter representation for each route parameter that did not find // a partner. foreach (var routeParameter in routeParameters) { context.Results.Add(new ApiParameterDescription() { Name = routeParameter.Key, RouteInfo = routeParameter.Value, Source = BindingSource.Path, }); } return(context.Results); }
private ApiDescription CreateApiDescription( ControllerActionDescriptor action, string httpMethod, string groupName) { var parsedTemplate = ParseTemplate(action); var apiDescription = new ApiDescription() { ActionDescriptor = action, GroupName = groupName, HttpMethod = httpMethod, RelativePath = GetRelativePath(parsedTemplate), }; var templateParameters = parsedTemplate?.Parameters?.ToList() ?? new List <TemplatePart>(); var parameterContext = new ApiParameterContext(_modelMetadataProvider, action, templateParameters); foreach (var parameter in GetParameters(parameterContext)) { apiDescription.ParameterDescriptions.Add(parameter); } var requestMetadataAttributes = GetRequestMetadataAttributes(action); var responseMetadataAttributes = GetResponseMetadataAttributes(action); // We only provide response info if we can figure out a type that is a user-data type. // Void /Task object/IActionResult will result in no data. var declaredReturnType = GetDeclaredReturnType(action); // Now 'simulate' an action execution. This attempts to figure out to the best of our knowledge // what the logical data type is using filters. var runtimeReturnType = GetRuntimeReturnType(declaredReturnType, responseMetadataAttributes); // We might not be able to figure out a good runtime return type. If that's the case we don't // provide any information about outputs. The workaround is to attribute the action. if (runtimeReturnType == typeof(void)) { // As a special case, if the return type is void - we want to surface that information // specifically, but nothing else. This can be overridden with a filter/attribute. apiDescription.ResponseType = runtimeReturnType; } else if (runtimeReturnType != null) { apiDescription.ResponseType = runtimeReturnType; apiDescription.ResponseModelMetadata = _modelMetadataProvider.GetMetadataForType(runtimeReturnType); var formats = GetResponseFormats(action, responseMetadataAttributes, runtimeReturnType); foreach (var format in formats) { apiDescription.SupportedResponseFormats.Add(format); } } // It would be possible here to configure an action with multiple body parameters, in which case you // could end up with duplicate data. foreach (var parameter in apiDescription.ParameterDescriptions.Where(p => p.Source == BindingSource.Body)) { var formats = GetRequestFormats(action, requestMetadataAttributes, parameter.Type); foreach (var format in formats) { apiDescription.SupportedRequestFormats.Add(format); } } return(apiDescription); }