Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add custom serializer to Swagger in my .Net core API

I'm using the JSONAPI specifications from jsonapi.org, then I'm using the JsonApiSerializer to accomplish the JSONAPI specification, so my response and request body looks like:

{    
    "data": {
    "type": "articles",
    "id": "stringId",
    "attributes": {
      "title": "JSON:API paints my bikeshed!"
    }
}

I have an entity "Article" it looks like:

public class Article
{
     public string Id { get; set; }
     public string title { get; set; }
}

Then I'm trying to use Swashbuckle Swagger for document my API, but in the Swagger UI my example Request and Response body looks like:

{
     "id": "string",
     "title": "string"
}

I think swagger is ignoring the JsonApiSerializer, there is a way to change the default serializer for swagger and use my own serializer?

My Startup.cs looks like:

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            this.Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc(
                    "v1",
                    new OpenApiInfo
                    {
                        Version = "v1",
                        Title = "HTT API",
                        Description = "HTT API provides methods to handle events",
                        Contact = new OpenApiContact
                        {
                            Name = "htt",
                            Email = "[email protected]",
                        },
                    });

                var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                c.IncludeXmlComments(xmlPath);
            });


            services.AddAPIDependencies(this.Configuration);
            services.AddControllers().AddNewtonsoftJson(
            options =>
            {
                var serializerSettings = new JsonApiSerializerSettings();
                options.SerializerSettings.ContractResolver = serializerSettings.ContractResolver;
                options.SerializerSettings.Converters.Add(new StringEnumConverter());
            });

            services.Configure<DatabaseSettings>(
            this.Configuration.GetSection(nameof(DatabaseSettings)));
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "HTT API V1");
            });

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
  • Net core 3.1
  • Swashbuckle.AspNetCore 5.0.0
like image 407
HTT Avatar asked Jan 24 '20 18:01

HTT


1 Answers

You can generate example swagger requests & responses using Swashbuckle.AspNetCore.Filters.

Example copied from one of the referenced blog posts:

[Route(RouteTemplates.DeliveryOptionsSearchByAddress)]
[SwaggerRequestExample(typeof(DeliveryOptionsSearchModel), typeof(DeliveryOptionsSearchModelExample))]
[SwaggerResponse(HttpStatusCode.OK, Type = typeof(DeliveryOptionsModel), Description = "Delivery options for the country found and returned successfully")]
[SwaggerResponseExample(HttpStatusCode.OK, typeof(DeliveryOptionsModelExample))]
[SwaggerResponse(HttpStatusCode.BadRequest, Type = typeof(ErrorsModel), Description = "An invalid or missing input parameter will result in a bad request")]
[SwaggerResponse(HttpStatusCode.InternalServerError, Type = typeof(ErrorsModel), Description = "An unexpected error occurred, should not return sensitive information")]
public async Task<IHttpActionResult> DeliveryOptionsForAddress(DeliveryOptionsSearchModel search)
{

Notice the use of various *Example models. Each such type should implement IExamplesProvider and generate sample data:

public class DeliveryOptionsSearchModelExample : IExamplesProvider
{
    public object GetExamples()
    {
        return new DeliveryOptionsSearchModel
        {
            Lang = "en-GB",
            Currency = "GBP",
            Address = new AddressModel
            {
                Address1 = "1 Gwalior Road",
                Locality = "London",
                Country = "GB",
                PostalCode = "SW15 1NP"
            },
            Items = new[]
            {
                new ItemModel
                {
                    ItemId = "ABCD",
                    ItemType = ItemType.Product,
                    Price = 20,
                    Quantity = 1,
                    RestrictedCountries = new[] { "US" }
                }
            }
        };
    }

Notice that your example provider should return instances of the type you specified in the SwaggerResponse attribute (e.g. DeliveryOptionsSearchModel).

Don’t forget to enable the ExamplesOperationFilter when you enable Swagger:

services.AddSwaggerGen(c =>
{
     c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
     c.OperationFilter<ExamplesOperationFilter>();
}

UPDATE

The documentation appears to be somewhat out of date. I had to do the following to make the example provider kick in:

services.AddSwaggerExamplesFromAssemblyOf<DeliveryOptionsSearchModelExample>();

.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo {Title = "Elsa", Version = "v1"});
    c.ExampleFilters();
})
like image 86
Sipke Schoorstra Avatar answered Sep 28 '22 02:09

Sipke Schoorstra