Reputation: 822
In our controller methods we use commands and queries. Instead of combining header, query, or environment parameters in each method, we want to use a custom model binder.
So instead of:
[Route("/api/persons")]
[HttpGet]
public async Task<ActionResult<PersonsResponseDTO>> GetPersons(
[FromHeader(Name = "Accept-Language")] preferredLanguages,
[FromHeader(Name = "If-Match")] string eTags,
[FromQuery(Name="lang"] string? selectedLanguage,
[FromQuery][CommaSeparated] IEnumerable<string> select,
[FromQuery][CommaSeparated] IEnumerable<string> sort,
int? pageNumber,
int? pageSize )
{
RequestAttributes requestAttributes = RequestAttributes.Create
.PreferredLanguages(preferredLanguages)
.eTags(eTags)
.IPAddress(httpContext.HttpContext.Connection.RemoteIpAddress)
.SelectedLanguage(selectedLanguage)
.Select(select)
.SortBy(sort)
.PageSize(pageSize)
.PageNumber(pageNumber);
var command = new GetPersonsCommand(requestAttributes);
Result<PersonsResponseDTO> commandResult = await _mediator.Send<PersonsResponseDTO>(command);
...
}
I want to construct RequestAttributes
in a custom model binder:
[Route("/api/persons")]
[HttpGet]
public async Task<ActionResult<PersonsResponseDTO>> GetPersons([ModelBinder(BinderType = typeof(RequestAttributesBinder))] RequestAttributes requestAttributes
{
var command = new GetPersonsCommand(requestAttributes);
Result<PersonsResponseDTO> commandResult = await _mediator.Send<PersonsResponseDTO>(command);
...
}
The parameters above are just examples. With a registered ModelBinderFactory the annotation can be removed:
public async Task<ActionResult<PersonsResponseDTO>> GetPersons(RequestAttributes requestAttributes)
This works fine.
My problem is with the Swashbuckle generator. Since the parameters are fetched from the query or headers in the custom model binder the generator doesn't take them into account and just displays a RequestAttributes
type as from the body. I tried to use annotations in the RequestAttributes
class, but it still didn't work.
I'm using Swashbuckle for the first time. I found out that I can configure the SwaggerGen in Startup.cs
. For example:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Elwis.API", Version = "v1" });
c.SupportNonNullableReferenceTypes();
c.MapType<Optional<Guid>>(() => new OpenApiSchema { Type = "string", Format = "uuid" });
}
I understand how I can map a type to a single Swagger type, but is it possible to map a complex type like RequestAttributes
to a set of Swagger types and indicate the source of the parameter (e.g. query, header)? If it is possible how can this be done?
Upvotes: 0
Views: 273
Reputation: 822
Even it doesn't answer the question directly, a solution to the problem is to place annotations directly in the RequestAttributes
class, instead of using a custom model binder.
public record RequestAttributes(
[FromHeader(Name = "Accept-Language")] preferredLanguages,
[FromHeader(Name = "If-Match")] string eTags,
[FromQuery(Name="lang"] string? selectedLanguage,
[FromQuery][CommaSeparated] IEnumerable<string> select,
[FromQuery][CommaSeparated] IEnumerable<string> sort,
[FromQuery] int? pageNumber,
[FromQuery] int? pageSize);
This works fine if all parameters come directly from header, query, path or body and no type conversions are required. If a custom model binder is needed to change type or add data that comes for example from HttpContext
more problems arise. I will post a seperate question for that.
Upvotes: 0