private async Task <bool> TryAddFileParameterAsync( OperationProcessorContext context, ExtendedApiParameterDescription extendedApiParameter) { var typeInfo = _settings.ReflectionService.GetDescription(extendedApiParameter.ParameterType, extendedApiParameter.Attributes, _settings); var isFileArray = IsFileArray(extendedApiParameter.ApiParameter.Type, typeInfo); var attributes = extendedApiParameter.Attributes .Union(extendedApiParameter.ParameterType.GetTypeInfo().GetCustomAttributes()); var hasSwaggerFileAttribute = attributes.Any(a => a.GetType().IsAssignableTo("SwaggerFileAttribute", TypeNameStyle.Name)); if (typeInfo.Type == JsonObjectType.File || typeInfo.Format == JsonFormatStrings.Binary || hasSwaggerFileAttribute || isFileArray) { await AddFileParameterAsync(context, extendedApiParameter, isFileArray).ConfigureAwait(false); return(true); } return(false); }
private async Task AddFileParameterAsync(OperationProcessorContext context, ExtendedApiParameterDescription extendedApiParameter, bool isFileArray) { var operationParameter = await CreatePrimitiveParameterAsync(context, extendedApiParameter).ConfigureAwait(false); InitializeFileParameter(operationParameter, isFileArray); context.OperationDescription.Operation.Parameters.Add(operationParameter); }
private void AddFileParameter(OperationProcessorContext context, ExtendedApiParameterDescription extendedApiParameter, bool isFileArray) { var operationParameter = CreatePrimitiveParameter(context, extendedApiParameter); InitializeFileParameter(operationParameter, isFileArray); context.OperationDescription.Operation.Parameters.Add(operationParameter); }
private OpenApiParameter CreatePrimitiveParameter( OperationProcessorContext context, ExtendedApiParameterDescription extendedApiParameter) { var contextualParameterType = extendedApiParameter.ParameterType .ToContextualType(extendedApiParameter.Attributes); var description = extendedApiParameter.GetDocumentation(); var operationParameter = context.DocumentGenerator.CreatePrimitiveParameter( extendedApiParameter.ApiParameter.Name, description, contextualParameterType); var exampleValue = extendedApiParameter.PropertyInfo != null? context.SchemaGenerator.GenerateExample(extendedApiParameter.PropertyInfo.ToContextualAccessor()) : null; var hasExampleValue = exampleValue != null; var hasDefaultValue = extendedApiParameter.ParameterInfo?.HasDefaultValue == true; if (hasExampleValue || hasDefaultValue) { var defaultValue = hasDefaultValue ? context.SchemaGenerator .ConvertDefaultValue(contextualParameterType, extendedApiParameter.ParameterInfo.DefaultValue) : null; if (_settings.SchemaType == SchemaType.Swagger2) { operationParameter.Default = defaultValue; operationParameter.Example = exampleValue; } else if (operationParameter.Schema.HasReference) { if (_settings.AllowReferencesWithProperties) { operationParameter.Schema = new JsonSchema { Default = defaultValue, Example = exampleValue, Reference = operationParameter.Schema, }; } else { operationParameter.Schema = new JsonSchema { Default = defaultValue, Example = exampleValue, OneOf = { operationParameter.Schema }, }; } } else { operationParameter.Schema.Default = defaultValue; operationParameter.Schema.Example = exampleValue; } } operationParameter.IsRequired = extendedApiParameter.IsRequired(_settings.RequireParametersWithoutDefault); return(operationParameter); }
private async Task AddBodyParameterAsync(OperationProcessorContext context, ExtendedApiParameterDescription extendedApiParameter) { var operation = context.OperationDescription.Operation; var parameterType = extendedApiParameter.ApiParameter.Type; if (parameterType.Name == "XmlDocument" || parameterType.InheritsFrom("XmlDocument", TypeNameStyle.Name)) { operation.Consumes = new List <string> { "application/xml" }; operation.Parameters.Add(new SwaggerParameter { Name = extendedApiParameter.ApiParameter.Name, Kind = SwaggerParameterKind.Body, Schema = new JsonSchema4 { Type = JsonObjectType.String }, IsNullableRaw = true, IsRequired = true, Description = await extendedApiParameter.GetDocumentationAsync().ConfigureAwait(false) }); } else if (parameterType.IsAssignableTo("System.IO.Stream", TypeNameStyle.FullName)) { operation.Consumes = new List <string> { "application/octet-stream" }; operation.Parameters.Add(new SwaggerParameter { Name = extendedApiParameter.ApiParameter.Name, Kind = SwaggerParameterKind.Body, Schema = new JsonSchema4 { Type = JsonObjectType.String, Format = JsonFormatStrings.Byte }, IsNullableRaw = true, IsRequired = true, Description = await extendedApiParameter.GetDocumentationAsync().ConfigureAwait(false) }); } else { var typeDescription = _settings.ReflectionService.GetDescription(extendedApiParameter.ApiParameter.Type, extendedApiParameter.Attributes, _settings); var operationParameter = new SwaggerParameter { Name = extendedApiParameter.ApiParameter.Name, Kind = SwaggerParameterKind.Body, IsRequired = true, // FromBody parameters are always required. IsNullableRaw = typeDescription.IsNullable, Description = await extendedApiParameter.GetDocumentationAsync().ConfigureAwait(false), Schema = await context.SchemaGenerator.GenerateWithReferenceAndNullability <JsonSchema4>( extendedApiParameter.ApiParameter.Type, extendedApiParameter.Attributes, isNullable : false, schemaResolver : context.SchemaResolver).ConfigureAwait(false) }; operation.Parameters.Add(operationParameter); } }
private async Task <SwaggerParameter> CreatePrimitiveParameterAsync( OperationProcessorContext context, ExtendedApiParameterDescription extendedApiParameter) { var operationParameter = await context.SwaggerGenerator.CreatePrimitiveParameterAsync( extendedApiParameter.ApiParameter.Name, await extendedApiParameter.GetDocumentationAsync().ConfigureAwait(false), extendedApiParameter.ParameterType, extendedApiParameter.Attributes).ConfigureAwait(false); if (extendedApiParameter.ParameterInfo?.HasDefaultValue == true) { operationParameter.Default = extendedApiParameter.ParameterInfo.DefaultValue; } operationParameter.IsRequired = extendedApiParameter.IsRequired(_settings.RequireParametersWithoutDefault); return(operationParameter); }
private async Task AddFileParameterAsync(OperationProcessorContext context, ExtendedApiParameterDescription extendedApiParameter, bool isFileArray) { var parameterDocumentation = string.Empty; if (extendedApiParameter.ParameterInfo != null) { parameterDocumentation = await extendedApiParameter.ParameterInfo.GetDescriptionAsync(extendedApiParameter.Attributes).ConfigureAwait(false); } else if (extendedApiParameter.PropertyInfo != null) { parameterDocumentation = await extendedApiParameter.PropertyInfo.GetDescriptionAsync(extendedApiParameter.Attributes).ConfigureAwait(false); } var operationParameter = await CreatePrimitiveParameterAsync( context, extendedApiParameter).ConfigureAwait(false); InitializeFileParameter(operationParameter, isFileArray); context.OperationDescription.Operation.Parameters.Add(operationParameter); }
private void AddFileParameter(OperationProcessorContext context, ExtendedApiParameterDescription extendedApiParameter, bool isFileArray) { if (_settings.SchemaType == SchemaType.Swagger2) { var operationParameter = CreatePrimitiveParameter(context, extendedApiParameter); operationParameter.Type = JsonObjectType.File; operationParameter.Kind = OpenApiParameterKind.FormData; if (isFileArray) { operationParameter.CollectionFormat = OpenApiParameterCollectionFormat.Multi; } context.OperationDescription.Operation.Parameters.Add(operationParameter); } else { var schema = CreateOrGetFormDataSchema(context); schema.Properties[extendedApiParameter.ApiParameter.Name] = CreateFormDataProperty(context, extendedApiParameter, schema); } }
private bool TryAddFileParameter( OperationProcessorContext context, ExtendedApiParameterDescription extendedApiParameter) { var typeInfo = _settings.ReflectionService.GetDescription(extendedApiParameter.ParameterType.ToContextualType(extendedApiParameter.Attributes), _settings); var isFileArray = IsFileArray(extendedApiParameter.ApiParameter.Type, typeInfo); var attributes = extendedApiParameter.Attributes .Union(extendedApiParameter.ParameterType.GetTypeInfo().GetCustomAttributes()); var hasSwaggerFileAttribute = attributes.FirstAssignableToTypeNameOrDefault("SwaggerFileAttribute", TypeNameStyle.Name) != null; if (typeInfo.Type == JsonObjectType.File || typeInfo.Format == JsonFormatStrings.Binary || hasSwaggerFileAttribute || isFileArray) { AddFileParameter(context, extendedApiParameter, isFileArray); return(true); } return(false); }
private OpenApiParameter CreatePrimitiveParameter( OperationProcessorContext context, ExtendedApiParameterDescription extendedApiParameter) { var contextualParameter = extendedApiParameter.ParameterType.ToContextualType(extendedApiParameter.Attributes); var description = extendedApiParameter.GetDocumentation(); var operationParameter = context.DocumentGenerator.CreatePrimitiveParameter( extendedApiParameter.ApiParameter.Name, description, contextualParameter); if (extendedApiParameter.ParameterInfo?.HasDefaultValue == true) { var defaultValue = context.SchemaGenerator .ConvertDefaultValue(contextualParameter, extendedApiParameter.ParameterInfo.DefaultValue); if (_settings.SchemaType == SchemaType.Swagger2) { operationParameter.Default = defaultValue; } else if (operationParameter.Schema.HasReference) { operationParameter.Schema = new JsonSchema { Default = defaultValue, OneOf = { operationParameter.Schema } }; } else { operationParameter.Schema.Default = defaultValue; } } operationParameter.IsRequired = extendedApiParameter.IsRequired(_settings.RequireParametersWithoutDefault); return(operationParameter); }
/// <summary>Processes the specified method information.</summary> /// <param name="operationProcessorContext"></param> /// <returns>true if the operation should be added to the Swagger specification.</returns> public async Task <bool> ProcessAsync(OperationProcessorContext operationProcessorContext) { if (!(operationProcessorContext is AspNetCoreOperationProcessorContext context)) { return(false); } var httpPath = context.OperationDescription.Path; var parameters = context.ApiDescription.ParameterDescriptions; var methodParameters = context.MethodInfo.GetParameters(); var position = 1; foreach (var apiParameter in parameters.Where(p => p.Source != null)) { // TODO: Provide extension point so that this can be implemented in the ApiVersionProcessor class var versionProcessor = _settings.OperationProcessors.TryGet <ApiVersionProcessor>(); if (versionProcessor != null && versionProcessor.IgnoreParameter && apiParameter.ModelMetadata?.DataTypeName == "ApiVersion") { continue; } var parameterDescriptor = apiParameter.TryGetPropertyValue <ParameterDescriptor>("ParameterDescriptor"); var parameterName = parameterDescriptor?.Name ?? apiParameter.Name; // In Mvc < 2.0, there isn't a good way to infer the attributes of a parameter with a IModelNameProvider.Name // value that's different than the parameter name. Additionally, ApiExplorer will recurse in to complex model bound types // and expose properties as top level parameters. Consequently, determining the property or parameter of an Api is best // effort attempt. var extendedApiParameter = new ExtendedApiParameterDescription { ApiParameter = apiParameter, Attributes = Enumerable.Empty <Attribute>(), ParameterType = apiParameter.Type }; var parameter = methodParameters.FirstOrDefault(m => m.Name == parameterName); if (parameter != null) { extendedApiParameter.ParameterInfo = parameter; extendedApiParameter.Attributes = parameter.GetCustomAttributes(); } else { var property = operationProcessorContext.ControllerType.GetProperty(parameterName, BindingFlags.Public | BindingFlags.Instance); if (property != null) { extendedApiParameter.PropertyInfo = property; extendedApiParameter.Attributes = property.GetCustomAttributes(); } } if (apiParameter.Type == null) { extendedApiParameter.ParameterType = typeof(string); if (apiParameter.Source == BindingSource.Path) { // ignore unused implicit path parameters if (!httpPath.ToLowerInvariant().Contains("{" + apiParameter.Name.ToLowerInvariant() + ":") && !httpPath.ToLowerInvariant().Contains("{" + apiParameter.Name.ToLowerInvariant() + "}")) { continue; } extendedApiParameter.Attributes = extendedApiParameter.Attributes.Concat(new[] { new NotNullAttribute() }); } } if (extendedApiParameter.Attributes.Any(a => a.GetType().Name == "SwaggerIgnoreAttribute")) { continue; } SwaggerParameter operationParameter = null; if (apiParameter.Source == BindingSource.Path) { operationParameter = await CreatePrimitiveParameterAsync(context, extendedApiParameter).ConfigureAwait(false); operationParameter.Kind = SwaggerParameterKind.Path; operationParameter.IsRequired = true; // apiParameter.RouteInfo?.IsOptional == false; context.OperationDescription.Operation.Parameters.Add(operationParameter); } else if (apiParameter.Source == BindingSource.Header) { operationParameter = await CreatePrimitiveParameterAsync(context, extendedApiParameter).ConfigureAwait(false); operationParameter.Kind = SwaggerParameterKind.Header; context.OperationDescription.Operation.Parameters.Add(operationParameter); } else if (apiParameter.Source == BindingSource.Query) { operationParameter = await CreatePrimitiveParameterAsync(context, extendedApiParameter).ConfigureAwait(false); operationParameter.Kind = SwaggerParameterKind.Query; context.OperationDescription.Operation.Parameters.Add(operationParameter); } else if (apiParameter.Source == BindingSource.Body) { operationParameter = await AddBodyParameterAsync(context, extendedApiParameter).ConfigureAwait(false); } else if (apiParameter.Source == BindingSource.Form) { operationParameter = await CreatePrimitiveParameterAsync(context, extendedApiParameter).ConfigureAwait(false); operationParameter.Kind = SwaggerParameterKind.FormData; context.OperationDescription.Operation.Parameters.Add(operationParameter); } else { if (await TryAddFileParameterAsync(context, extendedApiParameter).ConfigureAwait(false) == false) { operationParameter = await CreatePrimitiveParameterAsync(context, extendedApiParameter).ConfigureAwait(false); operationParameter.Kind = SwaggerParameterKind.Query; context.OperationDescription.Operation.Parameters.Add(operationParameter); } } if (parameter != null && operationParameter != null) { if (_settings.SchemaType == SchemaType.OpenApi3) { operationParameter.IsNullableRaw = null; } operationParameter.Position = position; ((Dictionary <ParameterInfo, SwaggerParameter>)operationProcessorContext.Parameters)[parameter] = operationParameter; position++; } } RemoveUnusedPathParameters(context.OperationDescription, httpPath); UpdateConsumedTypes(context.OperationDescription); EnsureSingleBodyParameter(context.OperationDescription); return(true); }
private OpenApiParameter AddBodyParameter(OperationProcessorContext context, ExtendedApiParameterDescription extendedApiParameter) { OpenApiParameter operationParameter; var contextualParameterType = extendedApiParameter.ParameterType .ToContextualType(extendedApiParameter.Attributes); var typeDescription = _settings.ReflectionService.GetDescription(contextualParameterType, _settings); var isNullable = _settings.AllowNullableBodyParameters && typeDescription.IsNullable; var operation = context.OperationDescription.Operation; var parameterType = extendedApiParameter.ParameterType; if (parameterType.Name == "XmlDocument" || parameterType.InheritsFromTypeName("XmlDocument", TypeNameStyle.Name)) { operation.TryAddConsumes("application/xml"); operationParameter = new OpenApiParameter { Name = extendedApiParameter.ApiParameter.Name, Kind = OpenApiParameterKind.Body, Schema = new JsonSchema { Type = JsonObjectType.String, IsNullableRaw = isNullable }, IsNullableRaw = isNullable, IsRequired = extendedApiParameter.IsRequired(_settings.RequireParametersWithoutDefault), Description = extendedApiParameter.GetDocumentation() }; } else if (parameterType.IsAssignableToTypeName("System.IO.Stream", TypeNameStyle.FullName)) { operation.TryAddConsumes("application/octet-stream"); operationParameter = new OpenApiParameter { Name = extendedApiParameter.ApiParameter.Name, Kind = OpenApiParameterKind.Body, Schema = new JsonSchema { Type = JsonObjectType.String, Format = JsonFormatStrings.Byte, IsNullableRaw = isNullable }, IsNullableRaw = isNullable, IsRequired = extendedApiParameter.IsRequired(_settings.RequireParametersWithoutDefault), Description = extendedApiParameter.GetDocumentation() }; } else // body from type { operationParameter = new OpenApiParameter { Name = extendedApiParameter.ApiParameter.Name, Kind = OpenApiParameterKind.Body, IsRequired = extendedApiParameter.IsRequired(_settings.RequireParametersWithoutDefault), IsNullableRaw = isNullable, Description = extendedApiParameter.GetDocumentation(), Schema = context.SchemaGenerator.GenerateWithReferenceAndNullability <JsonSchema>( contextualParameterType, isNullable, schemaResolver: context.SchemaResolver) }; } operation.Parameters.Add(operationParameter); return(operationParameter); }
private static JsonSchemaProperty CreateFormDataProperty(OperationProcessorContext context, ExtendedApiParameterDescription extendedApiParameter, JsonSchema schema) { return(context.SchemaGenerator.GenerateWithReferenceAndNullability <JsonSchemaProperty>( extendedApiParameter.ApiParameter.Type.ToContextualType(extendedApiParameter.Attributes), context.SchemaResolver)); }
/// <summary>Processes the specified method information.</summary> /// <param name="operationProcessorContext"></param> /// <returns>true if the operation should be added to the Swagger specification.</returns> public async Task <bool> ProcessAsync(OperationProcessorContext operationProcessorContext) { if (!(operationProcessorContext is AspNetCoreOperationProcessorContext context)) { return(false); } var httpPath = context.OperationDescription.Path; var parameters = context.ApiDescription.ParameterDescriptions; var methodParameters = context.MethodInfo.GetParameters(); foreach (var apiParameter in parameters.Where(p => p.Source != null)) { var parameterDescriptor = apiParameter.TryGetPropertyValue <ParameterDescriptor>("ParameterDescriptor"); var parameterName = parameterDescriptor?.Name ?? apiParameter.Name; // In Mvc < 2.0, there isn't a good way to infer the attributes of a parameter with a IModelNameProvider.Name // value that's different than the parameter name. Additionally, ApiExplorer will recurse in to complex model bound types // and expose properties as top level parameters. Consequently, determining the property or parameter of an Api is best // effort attempt. var extendedApiParameter = new ExtendedApiParameterDescription { ApiParameter = apiParameter, Attributes = Enumerable.Empty <Attribute>(), }; var parameter = methodParameters.FirstOrDefault(m => m.Name == parameterName); if (parameter != null) { extendedApiParameter.ParameterInfo = parameter; extendedApiParameter.Attributes = parameter.GetCustomAttributes(); } else { var property = operationProcessorContext.ControllerType.GetProperty(parameterName, BindingFlags.Public | BindingFlags.Instance); if (property != null) { extendedApiParameter.PropertyInfo = property; extendedApiParameter.Attributes = property.GetCustomAttributes(); } } if (extendedApiParameter.Attributes.Any(a => a.GetType().Name == "SwaggerIgnoreAttribute")) { continue; } if (apiParameter.Source == BindingSource.Path) { var operationParameter = await CreatePrimitiveParameterAsync(context, extendedApiParameter).ConfigureAwait(false); operationParameter.Kind = SwaggerParameterKind.Path; operationParameter.IsRequired = true; // Path is always required => property not needed if (_settings.SchemaType == SchemaType.Swagger2) { operationParameter.IsNullableRaw = false; } context.OperationDescription.Operation.Parameters.Add(operationParameter); } else if (apiParameter.Source == BindingSource.Header) { var operationParameter = await CreatePrimitiveParameterAsync(context, extendedApiParameter).ConfigureAwait(false); operationParameter.Kind = SwaggerParameterKind.Header; context.OperationDescription.Operation.Parameters.Add(operationParameter); } else if (apiParameter.Source == BindingSource.Query) { var operationParameter = await CreatePrimitiveParameterAsync(context, extendedApiParameter).ConfigureAwait(false); operationParameter.Kind = SwaggerParameterKind.Query; context.OperationDescription.Operation.Parameters.Add(operationParameter); } else if (apiParameter.Source == BindingSource.Body) { await AddBodyParameterAsync(context, extendedApiParameter).ConfigureAwait(false); } else if (apiParameter.Source == BindingSource.Form) { var operationParameter = await CreatePrimitiveParameterAsync(context, extendedApiParameter).ConfigureAwait(false); operationParameter.Kind = SwaggerParameterKind.FormData; context.OperationDescription.Operation.Parameters.Add(operationParameter); } else { if (await TryAddFileParameterAsync(context, extendedApiParameter).ConfigureAwait(false) == false) { var operationParameter = await CreatePrimitiveParameterAsync(context, extendedApiParameter).ConfigureAwait(false); operationParameter.Kind = SwaggerParameterKind.Query; context.OperationDescription.Operation.Parameters.Add(operationParameter); } } } RemoveUnusedPathParameters(context.OperationDescription, httpPath); UpdateConsumedTypes(context.OperationDescription); EnsureSingleBodyParameter(context.OperationDescription); return(true); }
private async Task <SwaggerParameter> AddBodyParameterAsync(OperationProcessorContext context, ExtendedApiParameterDescription extendedApiParameter) { SwaggerParameter operationParameter; var typeDescription = _settings.ReflectionService.GetDescription( extendedApiParameter.ParameterType, extendedApiParameter.Attributes, _settings); var isNullable = _settings.AllowNullableBodyParameters && typeDescription.IsNullable; var operation = context.OperationDescription.Operation; var parameterType = extendedApiParameter.ParameterType; if (parameterType.Name == "XmlDocument" || parameterType.InheritsFrom("XmlDocument", TypeNameStyle.Name)) { operation.Consumes = new List <string> { "application/xml" }; operationParameter = new SwaggerParameter { Name = extendedApiParameter.ApiParameter.Name, Kind = SwaggerParameterKind.Body, Schema = new JsonSchema4 { Type = JsonObjectType.String, IsNullableRaw = isNullable }, IsNullableRaw = isNullable, IsRequired = extendedApiParameter.IsRequired(_settings.RequireParametersWithoutDefault), Description = await extendedApiParameter.GetDocumentationAsync().ConfigureAwait(false) }; } else if (parameterType.IsAssignableTo("System.IO.Stream", TypeNameStyle.FullName)) { operation.Consumes = new List <string> { "application/octet-stream" }; operationParameter = new SwaggerParameter { Name = extendedApiParameter.ApiParameter.Name, Kind = SwaggerParameterKind.Body, Schema = new JsonSchema4 { Type = JsonObjectType.String, Format = JsonFormatStrings.Byte, IsNullableRaw = isNullable }, IsNullableRaw = isNullable, IsRequired = extendedApiParameter.IsRequired(_settings.RequireParametersWithoutDefault), Description = await extendedApiParameter.GetDocumentationAsync().ConfigureAwait(false) }; } else // body from type { operationParameter = new SwaggerParameter { Name = extendedApiParameter.ApiParameter.Name, Kind = SwaggerParameterKind.Body, IsRequired = extendedApiParameter.IsRequired(_settings.RequireParametersWithoutDefault), IsNullableRaw = isNullable, Description = await extendedApiParameter.GetDocumentationAsync().ConfigureAwait(false), Schema = await context.SchemaGenerator.GenerateWithReferenceAndNullabilityAsync <JsonSchema4>( extendedApiParameter.ParameterType, extendedApiParameter.Attributes, isNullable, schemaResolver : context.SchemaResolver).ConfigureAwait(false) }; } operation.Parameters.Add(operationParameter); return(operationParameter); }
/// <summary>Processes the specified method information.</summary> /// <param name="operationProcessorContext"></param> /// <returns>true if the operation should be added to the Swagger specification.</returns> public bool Process(OperationProcessorContext operationProcessorContext) { if (!(operationProcessorContext is AspNetCoreOperationProcessorContext context)) { return(false); } var httpPath = context.OperationDescription.Path; var parameters = context.ApiDescription.ParameterDescriptions; var methodParameters = context.MethodInfo?.GetParameters() ?? new ParameterInfo[0]; var position = 1; foreach (var apiParameter in parameters.Where(p => p.Source != null)) { // TODO: Provide extension point so that this can be implemented in the ApiVersionProcessor class var versionProcessor = _settings.OperationProcessors.TryGet <ApiVersionProcessor>(); if (versionProcessor != null && versionProcessor.IgnoreParameter && apiParameter.ModelMetadata?.DataTypeName == "ApiVersion") { continue; } // In Mvc < 2.0, there isn't a good way to infer the attributes of a parameter with a IModelNameProvider.Name // value that's different than the parameter name. Additionally, ApiExplorer will recurse in to complex model bound types // and expose properties as top level parameters. Consequently, determining the property or parameter of an Api is best // effort attempt. var extendedApiParameter = new ExtendedApiParameterDescription(_settings) { ApiParameter = apiParameter, Attributes = Enumerable.Empty <Attribute>(), ParameterType = apiParameter.Type }; ParameterInfo parameter = null; var propertyName = apiParameter.ModelMetadata?.PropertyName; var property = !string.IsNullOrEmpty(propertyName) ? apiParameter.ModelMetadata.ContainerType?.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance) : null; if (property != null) { extendedApiParameter.PropertyInfo = property; extendedApiParameter.Attributes = property.GetCustomAttributes(); } else { var parameterDescriptor = apiParameter.TryGetPropertyValue <ParameterDescriptor>("ParameterDescriptor"); var parameterName = parameterDescriptor?.Name ?? apiParameter.Name; parameter = methodParameters.FirstOrDefault(m => m.Name.ToLowerInvariant() == parameterName.ToLowerInvariant()); if (parameter != null) { extendedApiParameter.ParameterInfo = parameter; extendedApiParameter.Attributes = parameter.GetCustomAttributes(); } else if (operationProcessorContext.ControllerType != null) { parameterName = apiParameter.Name; property = operationProcessorContext.ControllerType.GetProperty(parameterName, BindingFlags.Public | BindingFlags.Instance); if (property != null) { extendedApiParameter.PropertyInfo = property; extendedApiParameter.Attributes = property.GetCustomAttributes(); } } } if (apiParameter.Type == null) { extendedApiParameter.ParameterType = typeof(string); if (apiParameter.Source == BindingSource.Path) { // ignore unused implicit path parameters if (!httpPath.ToLowerInvariant().Contains("{" + apiParameter.Name.ToLowerInvariant() + ":") && !httpPath.ToLowerInvariant().Contains("{" + apiParameter.Name.ToLowerInvariant() + "}")) { continue; } extendedApiParameter.Attributes = extendedApiParameter.Attributes.Concat(new[] { new NotNullAttribute() }); } } if (extendedApiParameter.Attributes.GetAssignableToTypeName("SwaggerIgnoreAttribute", TypeNameStyle.Name).Any()) { continue; } OpenApiParameter operationParameter = null; if (apiParameter.Source == BindingSource.Path || (apiParameter.Source == BindingSource.Custom && httpPath.Contains($"{{{apiParameter.Name}}}"))) { operationParameter = CreatePrimitiveParameter(context, extendedApiParameter); operationParameter.Kind = OpenApiParameterKind.Path; operationParameter.IsRequired = true; // apiParameter.RouteInfo?.IsOptional == false; context.OperationDescription.Operation.Parameters.Add(operationParameter); } else if (apiParameter.Source == BindingSource.Header) { operationParameter = CreatePrimitiveParameter(context, extendedApiParameter); operationParameter.Kind = OpenApiParameterKind.Header; context.OperationDescription.Operation.Parameters.Add(operationParameter); } else if (apiParameter.Source == BindingSource.Query) { operationParameter = CreatePrimitiveParameter(context, extendedApiParameter); operationParameter.Kind = OpenApiParameterKind.Query; context.OperationDescription.Operation.Parameters.Add(operationParameter); } else if (apiParameter.Source == BindingSource.Body) { operationParameter = AddBodyParameter(context, extendedApiParameter); } else if (apiParameter.Source == BindingSource.Form) { if (_settings.SchemaType == SchemaType.Swagger2) { operationParameter = CreatePrimitiveParameter(context, extendedApiParameter); operationParameter.Kind = OpenApiParameterKind.FormData; context.OperationDescription.Operation.Parameters.Add(operationParameter); } else { var schema = CreateOrGetFormDataSchema(context); schema.Properties[extendedApiParameter.ApiParameter.Name] = CreateFormDataProperty(context, extendedApiParameter, schema); } } else { if (TryAddFileParameter(context, extendedApiParameter) == false) { operationParameter = CreatePrimitiveParameter(context, extendedApiParameter); operationParameter.Kind = OpenApiParameterKind.Query; context.OperationDescription.Operation.Parameters.Add(operationParameter); } } if (operationParameter != null) { operationParameter.Position = position; position++; if (_settings.SchemaType == SchemaType.OpenApi3) { operationParameter.IsNullableRaw = null; } if (parameter != null) { if (_settings.GenerateOriginalParameterNames && operationParameter.Name != parameter.Name) { operationParameter.OriginalName = parameter.Name; } ((Dictionary <ParameterInfo, OpenApiParameter>)operationProcessorContext.Parameters)[parameter] = operationParameter; } } } ApplyOpenApiBodyParameterAttribute(context.OperationDescription, context.MethodInfo); RemoveUnusedPathParameters(context.OperationDescription, httpPath); UpdateConsumedTypes(context.OperationDescription); EnsureSingleBodyParameter(context.OperationDescription); return(true); }