I have build a Web API application using .NET 6, Mediatr, and Swashbuckle ASPNetCore. I am using nTier structure, so there is more than one project for my solutions with my Web API project having reference to a class library project that consist business logic.
The problem is, swagger-ui wont generate query string parameter to be shown on browser while everything else is normal. And another interesting part is, when using Postman, you can pass the query string key and value and it works like a charm.
Here is my request body model from class library project:
using MediatR;
using Microsoft.AspNetCore.Http;
using System.Reflection;
namespace Rest.API.Application
{
public class FindRequest : IRequest<FindResponse>
{
public string firstName { get; init; }
public string lastName { get; set; }
public static ValueTask<FindRequest> BindAsync(HttpContext context, ParameterInfo parameter)
{
FindRequest result = new()
{
firstName = context.Request.Query["firstName"],
lastName = context.Request.Query["lastName"]
};
return ValueTask.FromResult(result);
}
}
}
Here is the Endpoint class from Web API project:
using MediatR;
using Rest.API.Application;
namespace Rest.API.Core
{
public interface IEndpoint
{
void ConfigureApplication(WebApplication app);
}
public class Endpoint : IEndpoint
{
public void ConfigureApplication(WebApplication app)
{
app.MapGet("employee/{id}", async (IMediator mediator, string id) => await mediator.Send(new GetRequest(id)));
app.MapGet("employee", async (IMediator mediator, FindRequest request) => await mediator.Send(request));
}
}
}
My endpoint extension class to wrap all endpoint so I can easily register all endpoint to Program.cs:
using Microsoft.OpenApi.Models;
using System.Reflection;
namespace Rest.API.Core
{
public static class EndpointExtension
{
public static void AddEndpoint(this IServiceCollection service, params Type[] types)
{
var endpoints = new List<IEndpoint>();
foreach (var type in types)
{
endpoints.AddRange(type.Assembly.ExportedTypes
.Where(x => typeof(IEndpoint).IsAssignableFrom(x)
&& !x.IsInterface
&& !x.IsAbstract)
.Select(Activator.CreateInstance)
.Cast<IEndpoint>());
}
service.AddSingleton(endpoints as IReadOnlyCollection<IEndpoint>);
}
public static void UseEndpoint(this WebApplication app)
{
var endpoints = app.Services.GetRequiredService<IReadOnlyCollection<IEndpoint>>();
foreach (var endpoint in endpoints)
{
endpoint.ConfigureApplication(app);
}
}
}
public static class SwaggerExtension
{
public static void ConfigureSwagger(this IServiceCollection services)
{
services.AddEndpointsApiExplorer();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("dev", new OpenApiInfo
{
Title = "Sample Web API Core",
Version = $"DEV-{Environment.Version.Major}.{Environment.Version.Minor}.{DateTime.Now:yyyyMMddHHmmss}",
Description = "Sample Web API"
});
string xmlDocFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
string xmlDocPath = Path.Combine(AppContext.BaseDirectory, xmlDocFile);
c.IncludeXmlComments(xmlDocPath);
});
}
public static void UseSwaggerApp(this WebApplication app)
{
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(x =>
{
x.SwaggerEndpoint("/swagger/dev/swagger.json", "Rest.API.Core");
x.RoutePrefix = "swagger";
x.DocumentTitle = "Sample Web API";
});
}
}
}
public static class CORSExtension
{
public static void ConfigureCORS(this IServiceCollection services)
{
services.AddCors(options =>
{
//NOT FOR PRODUCTION
options.AddPolicy("AllowAnyOrigin", builder =>
{
builder.AllowAnyOrigin();
builder.AllowAnyMethod();
builder.AllowAnyHeader();
});
});
}
}
}
Finally, my Program.cs:
using MediatR;
using Rest.API.Application;
using Rest.API.Core;
using System.Reflection;
var builder = WebApplication.CreateBuilder(args);
#region SERVICES
builder.Services.ConfigureCORS();
builder.Services.AddMediatR(typeof(GetHandler).GetTypeInfo().Assembly);
builder.Services.ConfigureSwagger();
builder.Services.AddEndpoint(typeof(IEndpoint)); //Register all endpoint(controller) that implementing IEndpoint.
#endregion
var app = builder.Build();
#region PIPELINE
app.UseSwaggerApp();
app.UseEndpoint();
app.UseCors("AllowAnyOrigin");
app.UseHttpsRedirection();
#endregion
app.Run();
For the complete sample project you can get from here.
So, any idea how show the query string model on swagger-ui?
Thank You.
There's no way for your model to express how it's being bound from the request. This is a gap being resolved in .NET 7, see https://github.com/dotnet/aspnetcore/issues/40646.
To accomplish this with .NET 6, you can https://www.nuget.org/packages/MinimalApis.Extensions