Reputation: 822
Instead of using a bunch of different parameters in a controller method, I want to consolidate all parameters into one class.
Additionally, environment parameters will be collected from HttpContext
and are bound via a custom model binder. Those environment parameters should be excluded from Swagger UI.
Test example:
[HttpPost]
public ActionResult Post(TestDetails testDetails)
{
return Ok();
}
public record TestDetails
{
[FromBody]
public Body? body { get; init; }
[SwaggerIgnore]
public string? ignoreMe { get; init; }
[SwaggerIgnore, ModelBinder(typeof(EnvironmentBinder))]
public IPAddress? ipAddress { get; init; }
[SwaggerIgnore, ModelBinder(typeof(DefaultValueBinder))]
public string? Default { get; init; }
[FromHeader(Name = "Accept-Language")]
public string? preferredLanguages { get; init; }
[FromQuery]
public string? selectedLanguage { get; init; }
}
public record Body
{
public string? name { get; init; }
public string? stageName { get; init; }
}
It is this not documented at Microsoft Learn, but to make collecting parameters in one class possible, either the following setting has to be added:
builder.Services.AddMvc().ConfigureApiBehaviorOptions(options => {
options.SuppressInferBindingSourcesForParameters = true;
});
or from probably .NET 6 on the method parameter can be attributed with [FromQuery]
to make it work:
[HttpPost]
public ActionResult Post([FromQuery] TestDetails testDetails)
{
return Ok();
}
Calling the API with Postman works fine, but I'm not able to ignore the parameters in Swagger UI. I tried various approaches:
The stackoverflow post How to configure Swashbuckle to ignore property on model shows how to use a custom [SwaggerIgnore]
attribute. The post is older and I don't understand which version is the one for the current version of Swashbuckle and ASP.NET Core. It also seems that the versions only work with properties in the body, but not with properties in a separate class.
The stackoverflow post Hide parameter from Swagger (Swashbuckle) shows a solution that works for parameters and not only properties in the body. It works when the parameter is in the method parameter list, but not when parameters are collected in one class. SwaggerIgnore
in my example above is ignored.
I also tried the Swagger Annotation extension library with the attribute [SwaggerSchema(ReadOnly = true)]
. It works fine on body parameters, but again not in my example above.
How can I achieve to collect all properties in one class while excluding some from Swagger UI? I don't want to us [JsonIgnore]
because it is a different concern.
Versions:
Swashbuckle.AspNetCore 6.5.0
Swashbuckle.AspNetCore.Annotations 6.5.0
.NET 7 / ASP.NET Core
Upvotes: 0
Views: 1351
Reputation: 822
After a thorough test (https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/2652) of the options [FromServices]
, ISchemaFilter
, IOperationFilter
, and [SwaggerSchema(ReadOnly = true)]
from the Annotation Extension library I found a solution to the stated problem extending the solution by Ricardo Yanez (Hide parameter from Swagger (Swashbuckle)).
I figured out that in a controller method parameters are considered ParameterAttributes
, but inside the class they are PropertyAttributes
and therefore not selected in the code by Ricardo.
The solution to my problem is to change metadata.Attributes.ParameterAttributes
to metadata.Attributes.Attributes
. Now both parameter and property attributes are selected and removed.
The IOperationFilter
doesn't remove the parameter from the displayed model in SwaggerUI. It requires an additional ISchemaFilter
or the usage of [SwaggerSchema(ReadOnly = true)]
.
Comments:
Even this is a solution to the stated problem it does not work to hide a parameter inside the [FromBody]
class. I haven't figured out a way to extend the solution to work generically, yet. To hide properties in the body class ISchemaFilter
or [SwaggerSchema(ReadOnly = true)]
can be used.
Using a single class to model bind all parameters currently has issues and limitations (https://github.com/dotnet/AspNetCore.Docs/issues/29295). This is why I had to use a separate class for the body parameters.
Upvotes: 1