mattfrear/Swashbuckle.Examples

SwaggerRequestExample not showing on Swagger UI

rafaelsanchezsouza opened this issue · 9 comments

I've been trying to document this API with Swashbuckle.Example. I have followed this tutorial. The SwaggerResponseExample attribute works but the SwaggerRequestExample is not rendered. I have also checked the json in /docs/v1 and again the Response Example was present but not the Request Example.

What's going on?
How do I debug this?

Thanks a lot!

Controller.cs

[System.Web.Http.HttpPost]
[SwaggerOperation(OperationId = "SAAT_ciclotarifario_GetById")]
[SwaggerResponseExample(HttpStatusCode.OK, typeof(GrupoFtFronteiraPostResponseExamples))]
[SwaggerRequestExample(typeof(GrupoFtFronteira), typeof(GrupoFtFronteiraPostRequestExamples))]
[ActionName("GrupoFtFronteira")]
public HttpResponseMessage PostGrupoFtFronteira([FromBody] GrupoFtFronteira requestBody)
{
    try
    {
        var novoGrupoFtFronteira = _grupofronteiraNegocio.PostGrupoFtFronteira(requestBody);

        return new HttpResponseMessage(HttpStatusCode.OK)
        {
            ReasonPhrase = "Success",
            Content = new StringContent("idGrupoFtFronteira: " + novoGrupoFtFronteira.IdGrupoftfronteira, System.Text.Encoding.UTF8, "application/json")
        };
    }
    catch (Exception e)
    {
        var msg = new HttpResponseMessage(HttpStatusCode.InternalServerError)
        {
            ReasonPhrase = "Internal Server Error",
            Content = new StringContent("Erro ao tentar atualizar dados. " + e.Message, System.Text.Encoding.UTF8, "application/json")
        };

        throw new HttpResponseException(msg);
    }
}

SwaggerConfig.cs

public class SwaggerConfig
{
    public static void Register()
    {
        var thisAssembly = typeof(SwaggerConfig).Assembly;

        GlobalConfiguration.Configuration
            .EnableSwagger(c =>
                {
                    c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
                    c.DocumentFilter<SwaggerFilterOutControllers>();

                    c.SingleApiVersion("v1", "ons.sact.servico.controller");

                    c.ApiKey("Authorization")
                    .Description("JWT token")
                    .Name("Bearer")
                    .In("header");

                    c.OperationFilter<ExamplesOperationFilter>();
                })
            .EnableSwaggerUi(c =>
                {
                    c.EnableApiKeySupport("apiKey", "header");
                });
    }
}

Hmm, I'm not sure, your code looks correct as far as I can see.

What does your GrupoFtFronteiraPostRequestExamples look like?

Maybe try calling c.OperationFilter<ExamplesOperationFilter>(); earlier, i.e. before c.ResolveConflictingActions()

Thanks for the answer!
I just tried it without any success...

I've sent the same question in Stackoverflow and I got asked about the ExamplesOperationFilter Class, which is an interesting question because I believe maybe there isn't any in my code

GrupoFtFronteiraPostRequestExamples.cs

public class GrupoFtFronteiraPostRequestExamples : IExamplesProvider
{
    public object GetExamples()
    {
        return new GrupoFtFronteira
        {
            Nome = "Test Post GrupoFtFronteira",
            IdInstalacao = "string",
            DataAtivacao = DateTime.Now,
            DataDesativacao = DateTime.Now,
            FTList = new List<int>() { int1,int2 }
        };
    }
}

(I omitted the real values because it might be sensible information)

PS. I've just tried to copy and paste this ExampleOperationFilter, it compiled, but nothing changed in the behaviour....

What version of .NET are you using? i.e. .NET Framework, .NET Core, or something else?

Can you post a screenshot which shows the Request example is not working?

I'm using .NET v4.5.1

Follows a printscreen, you can see that the ResponseExample is working...

image

I don't know how [SwaggerResponseExample] is working, since you're missing the [SwaggerResponse] attribute which is needed.

I don't normally do this for strangers on the internet, but you must have caught me in a good mood. I just created a new .NET Framework web api.

I don't know what you're doing wrong, but this works fine for me
image

Controller.cs:

using Swashbuckle.Examples;
using Swashbuckle.Swagger.Annotations;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace WebApplication5.Controllers
{
    public class ValuesController : ApiController
    {
        /// <summary>
        /// Creates a new grupo, whatever that is.
        /// </summary>
        /// <param name="requestBody"></param>
        /// <returns></returns>
        [HttpPost]
        [SwaggerOperation(OperationId = "SAAT_ciclotarifario_GetById")]
        [SwaggerResponse(HttpStatusCode.OK, Type = typeof(GrupoFtFronteiraResponse))]
        [SwaggerResponseExample(HttpStatusCode.OK, typeof(GrupoFtFronteiraPostResponseExamples))]
        [SwaggerRequestExample(typeof(GrupoFtFronteira), typeof(GrupoFtFronteiraPostRequestExamples))]
        [ActionName("GrupoFtFronteira")]
        
        public HttpResponseMessage PostGrupoFtFronteira([FromBody] GrupoFtFronteira requestBody)
        {
            return new HttpResponseMessage(HttpStatusCode.OK)
            {
                ReasonPhrase = "Success",
                Content = new StringContent("idGrupoFtFronteira: ", System.Text.Encoding.UTF8, "application/json")
            };
        }
    }

    /// <summary>
    /// Frontier group blah
    /// </summary>
    public class GrupoFtFronteira
    {
        /// <summary>
        /// The geezer's name
        /// </summary>
        public string Nome { get; set; }

        public string IdInstalacao { get; set; }

        public DateTime DataAtivacao { get; set; }

        public DateTime DataDesativacao { get; set; }

        public List<int> FTList { get; set; }
    }

    public class GrupoFtFronteiraResponse
    {
        public int IdGrupoFtFronteira { get; set; }
    }

    public class GrupoFtFronteiraPostResponseExamples : IExamplesProvider
    {
        public object GetExamples()
        {
            return new GrupoFtFronteiraResponse { IdGrupoFtFronteira = 666 };
        }
    }

    public class GrupoFtFronteiraPostRequestExamples : IExamplesProvider
    {
        public object GetExamples()
        {
            return new GrupoFtFronteira
            {
                Nome = "Test Post GrupoFtFronteira",
                IdInstalacao = "hello",
                DataAtivacao = DateTime.Now,
                DataDesativacao = DateTime.Now,
                FTList = new List<int>() { 7, 8 }
            };
        }
    }
}

SwaggerConfig.cs:

using System.Web.Http;
using WebActivatorEx;
using WebApplication5;
using Swashbuckle.Application;
using System;
using System.Reflection;
using System.IO;
using Swashbuckle.Examples;

[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]

namespace WebApplication5
{
    public class SwaggerConfig
    {
        public static void Register()
        {
            var thisAssembly = typeof(SwaggerConfig).Assembly;

            GlobalConfiguration.Configuration
                .EnableSwagger(c =>
                    {
                        c.SingleApiVersion("v1", "WebApplication5");

                        // var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
                        // var commentsFileName = Assembly.GetExecutingAssembly().GetName().Name + ".XML";
                        // var commentsFile = Path.Combine(baseDirectory, commentsFileName);

                        // c.IncludeXmlComments(commentsFile);

                        c.OperationFilter<ExamplesOperationFilter>();
                    })
                .EnableSwaggerUi(c =>
                    {
                    });
        }
    }
}

I found it!

It seems that I was supposed to fill the Request Type inside this foreach ( KeyValuePair<string, Swashbuckle.Swagger.Schema> definition in schemaRegistry.Definitions)

There's probably a better way around this, since this is a hardcoded solution...

Anyway, thanks a lot for spending your time with a random internet guy!

Great work you are doing there!

SwaggerFilterOutControllers.cs

void IDocumentFilter.Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
    {
        var models = new List<string>();

        foreach (ApiDescription apiDescription in apiExplorer.ApiDescriptions)
        {
            Console.WriteLine(apiDescription.Route.RouteTemplate);

            if (apiDescription.RelativePathSansQueryString().StartsWith("api/v1/")
                && !(apiDescription.RelativePathSansQueryString().StartsWith("api/v1/SAAT/ciclotarifario")
                || apiDescription.RelativePathSansQueryString().StartsWith("api/v1/SAAT/GrupoFtFronteira")))
            {
                string path = "/" + apiDescription.RelativePathSansQueryString();
                swaggerDoc.paths.Remove(path);
            }
        }

        foreach ( KeyValuePair<string, Swashbuckle.Swagger.Schema> definition in schemaRegistry.Definitions)
        {
            Console.WriteLine(definition.Key);

            if (!(definition.Key.Equals("CiclotarifarioPostRequest") ||
            definition.Key.Equals("GrupoFtFronteira")
            ))
            {
                models.Add(definition.Key.ToString());
            }
        }
        foreach (string modelo in models)
        {
            swaggerDoc.definitions.Remove(modelo);
        }


        CreateHeadersDict();

        var allOtherPaths = swaggerDoc.paths
            .Select(entry => entry.Value)
            .ToList();

        foreach (var path in allOtherPaths)
        {
            AddHeadersToPath(path, HeaderType.TokenAuth);
        }
    }

Oh, well that's quite annoying. Your custom SwaggerFilterOutControllers filter was the problem. Glad to hear you figured it out.

Also, I'll mention you can use [ApiExplorerSettings(IgnoreApi = true)] on your controller or action if you want to remove certain operations from the Swagger. Then you can delete your SwaggerFilterOutControllers filter.

Source: domaindrivendev/Swashbuckle.WebApi#153 (comment)