Skip to content

Commit

Permalink
Support [SwaggerIgnore] with [FromForm] requests (domaindrivendev#2928)
Browse files Browse the repository at this point in the history
* Add possibility to Ignore properties in FromForm Request with SwaggerIgnore attribute

* Refactor the logic

* Added UnitTests

---------

Co-authored-by: Javier García de la Noceda Argüelles <[email protected]>
  • Loading branch information
jgarciadelanoceda and Javier García de la Noceda Argüelles authored Jun 1, 2024
1 parent 44005e6 commit c1ee0e4
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -547,18 +547,22 @@ private OpenApiSchema GenerateSchemaFromFormParameters(
? formParameter.Name.ToCamelCase()
: formParameter.Name;

var schema = (formParameter.ModelMetadata != null)
var propertyInfo = formParameter.PropertyInfo();
if (!propertyInfo?.HasAttribute<SwaggerIgnoreAttribute>() ?? true)
{
var schema = (formParameter.ModelMetadata != null)
? GenerateSchema(
formParameter.ModelMetadata.ModelType,
schemaRepository,
formParameter.PropertyInfo(),
propertyInfo,
formParameter.ParameterInfo())
: new OpenApiSchema { Type = "string" };

properties.Add(name, schema);
properties.Add(name, schema);

if (formParameter.IsRequiredParameter())
requiredPropertyNames.Add(name);
if (formParameter.IsRequiredParameter())
requiredPropertyNames.Add(name);
}
}

return new OpenApiSchema
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Swashbuckle.AspNetCore.Annotations;
using Swashbuckle.AspNetCore.SwaggerGen.Test.Fixtures;

namespace Swashbuckle.AspNetCore.SwaggerGen.Test
{
Expand All @@ -27,28 +28,28 @@ public void ActionWithRouteNameMetadata()
public void ActionWithObsoleteAttribute()
{ }

public void ActionWithParameterWithBindNeverAttribute([BindNever]string param)
public void ActionWithParameterWithBindNeverAttribute([BindNever] string param)
{ }

public void ActionWithParameterWithRequiredAttribute([Required]string param)
public void ActionWithParameterWithRequiredAttribute([Required] string param)
{ }

public void ActionWithParameterWithBindRequiredAttribute([BindRequired]string param)
public void ActionWithParameterWithBindRequiredAttribute([BindRequired] string param)
{ }

public void ActionWithIntParameter(int param)
{ }

public void ActionWithIntParameterWithRangeAttribute([Range(1, 12)]int param)
public void ActionWithIntParameterWithRangeAttribute([Range(1, 12)] int param)
{ }

public void ActionWithIntParameterWithDefaultValue(int param = 1)
{ }

public void ActionWithIntParameterWithDefaultValueAttribute([DefaultValue(3)]int param)
public void ActionWithIntParameterWithDefaultValueAttribute([DefaultValue(3)] int param)
{ }

public void ActionWithIntParameterWithRequiredAttribute([Required]int param)
public void ActionWithIntParameterWithRequiredAttribute([Required] int param)
{ }

public void ActionWithIntParameterWithSwaggerIgnoreAttribute([SwaggerIgnore] int param)
Expand Down Expand Up @@ -101,10 +102,12 @@ public FileContentResult ActionWithFileResult()
public void ActionWithSwaggerIgnoreAttribute()
{ }

public void ActionHavingIFormFileParamWithFromFormAtribute([FromForm] IFormFile fileUpload)
public void ActionHavingIFormFileParamWithFromFormAttribute([FromForm] IFormFile fileUpload)
{ }

public void ActionHavingFromFormAtributeButNotWithIFormFile([FromForm] string param1, IFormFile param2)
public void ActionHavingFromFormAttributeButNotWithIFormFile([FromForm] string param1, IFormFile param2)
{ }
public void ActionHavingFromFormAttributeWithSwaggerIgnore([FromForm] SwaggerIngoreAnnotatedType param1)
{ }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;

namespace Swashbuckle.AspNetCore.SwaggerGen.Test.Fixtures
{
internal class FakeICompositeMetadataDetailsProvider : ICompositeMetadataDetailsProvider
{
public void CreateBindingMetadata(BindingMetadataProviderContext context)
{
throw new NotImplementedException();
}

public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
{
throw new NotImplementedException();
}

public void CreateValidationMetadata(ValidationMetadataProviderContext context)
{
throw new NotImplementedException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.AspNetCore.Routing;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen.Test.Fixtures;
using Swashbuckle.AspNetCore.TestSupport;
using Xunit;

Expand Down Expand Up @@ -1593,14 +1595,14 @@ public void GetSwagger_GeneratesSwaggerDocument_ThrowsIfHttpMethodNotSupported(s
public void GetSwagger_Throws_Exception_When_FromForm_Attribute_Used_With_IFormFile()
{
var parameterInfo = typeof(FakeController)
.GetMethod(nameof(FakeController.ActionHavingIFormFileParamWithFromFormAtribute))
.GetMethod(nameof(FakeController.ActionHavingIFormFileParamWithFromFormAttribute))
.GetParameters()[0];

var subject = Subject(
apiDescriptions: new[]
{
ApiDescriptionFactory.Create<FakeController>(
c => nameof(c.ActionHavingIFormFileParamWithFromFormAtribute),
c => nameof(c.ActionHavingIFormFileParamWithFromFormAttribute),
groupName: "v1",
httpMethod: "POST",
relativePath: "resource",
Expand All @@ -1623,18 +1625,18 @@ public void GetSwagger_Throws_Exception_When_FromForm_Attribute_Used_With_IFormF
public void GetSwagger_Works_As_Expected_When_FromForm_Attribute_Not_Used_With_IFormFile()
{
var paraminfo = typeof(FakeController)
.GetMethod(nameof(FakeController.ActionHavingFromFormAtributeButNotWithIFormFile))
.GetMethod(nameof(FakeController.ActionHavingFromFormAttributeButNotWithIFormFile))
.GetParameters()[0];

var fileUploadParameterInfo = typeof(FakeController)
.GetMethod(nameof(FakeController.ActionHavingFromFormAtributeButNotWithIFormFile))
.GetMethod(nameof(FakeController.ActionHavingFromFormAttributeButNotWithIFormFile))
.GetParameters()[1];

var subject = Subject(
apiDescriptions: new[]
{
ApiDescriptionFactory.Create<FakeController>(
c => nameof(c.ActionHavingFromFormAtributeButNotWithIFormFile),
c => nameof(c.ActionHavingFromFormAttributeButNotWithIFormFile),
groupName: "v1",
httpMethod: "POST",
relativePath: "resource",
Expand Down Expand Up @@ -1668,6 +1670,59 @@ public void GetSwagger_Works_As_Expected_When_FromForm_Attribute_Not_Used_With_I
Assert.Equal("param2", operation.Parameters[1].Name);
}

[Fact]
public void GetSwagger_Works_As_Expected_When_FromForm_Attribute_With_SwaggerIgnore()
{
var propertyIgnored = typeof(SwaggerIngoreAnnotatedType).GetProperty(nameof(SwaggerIngoreAnnotatedType.IgnoredString));
var modelMetadataIgnored = new DefaultModelMetadata(
new DefaultModelMetadataProvider(new FakeICompositeMetadataDetailsProvider()),
new FakeICompositeMetadataDetailsProvider(),
new DefaultMetadataDetails(ModelMetadataIdentity.ForProperty(propertyIgnored, typeof(string), typeof(SwaggerIngoreAnnotatedType)), ModelAttributes.GetAttributesForProperty(typeof(SwaggerIngoreAnnotatedType), propertyIgnored)));

var propertyNotIgnored = typeof(SwaggerIngoreAnnotatedType).GetProperty(nameof(SwaggerIngoreAnnotatedType.NotIgnoredString));
var modelMetadataNotIgnored = new DefaultModelMetadata(
new DefaultModelMetadataProvider(new FakeICompositeMetadataDetailsProvider()),
new FakeICompositeMetadataDetailsProvider(),
new DefaultMetadataDetails(ModelMetadataIdentity.ForProperty(propertyNotIgnored, typeof(string), typeof(SwaggerIngoreAnnotatedType)), ModelAttributes.GetAttributesForProperty(typeof(SwaggerIngoreAnnotatedType), propertyNotIgnored)));
var subject = Subject(
apiDescriptions: new[]
{
ApiDescriptionFactory.Create<FakeController>(
c => nameof(c.ActionHavingFromFormAttributeWithSwaggerIgnore),
groupName: "v1",
httpMethod: "POST",
relativePath: "resource",
parameterDescriptions: new[]
{
new ApiParameterDescription
{
Name = nameof(SwaggerIngoreAnnotatedType.IgnoredString),
Source = BindingSource.Form,
Type = typeof(string),
ModelMetadata = modelMetadataIgnored
},
new ApiParameterDescription
{
Name = nameof(SwaggerIngoreAnnotatedType.NotIgnoredString),
Source = BindingSource.Form,
Type = typeof(string),
ModelMetadata = modelMetadataNotIgnored
}
})
}
);
var document = subject.GetSwagger("v1");

var operation = document.Paths["/resource"].Operations[OperationType.Post];
Assert.NotNull(operation.RequestBody);
Assert.Equal(new[] { "multipart/form-data" }, operation.RequestBody.Content.Keys);
var mediaType = operation.RequestBody.Content["multipart/form-data"];
Assert.NotNull(mediaType.Schema);
Assert.Equal(new[] { nameof(SwaggerIngoreAnnotatedType.NotIgnoredString) }, mediaType.Schema.Properties.Keys);
Assert.NotNull(mediaType.Encoding);
Assert.Equal(new[] { nameof(SwaggerIngoreAnnotatedType.NotIgnoredString) }, mediaType.Encoding.Keys);
}

private static SwaggerGenerator Subject(
IEnumerable<ApiDescription> apiDescriptions,
SwaggerGeneratorOptions options = null,
Expand Down
23 changes: 19 additions & 4 deletions test/WebSites/Basic/Controllers/FromFormParamsController.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;

namespace Basic.Controllers
{
public class FromFormParamsController
{
[HttpPost("registrations")]
[Consumes("application/x-www-form-urlencoded")]
public IActionResult PostForm([FromForm]RegistrationForm form)
public IActionResult PostForm([FromForm] RegistrationForm form)
{
throw new System.NotImplementedException();
}

[HttpPost("registrationsWithIgnoreProperties")]
public IActionResult PostFormWithIgnoredProperties([FromForm] RegistrationFormWithIgnoredProperties form)
{
throw new System.NotImplementedException();
}
Expand All @@ -19,4 +26,12 @@ public class RegistrationForm

public IEnumerable<int> PhoneNumbers { get; set; }
}
}

public class RegistrationFormWithIgnoredProperties
{
[SwaggerIgnore, FromForm(Name = "internal_Name")]
public string Name { get; set; }

public IEnumerable<int> PhoneNumbers { get; set; }
}
}

0 comments on commit c1ee0e4

Please sign in to comment.