12

I have had no problem getting a json file in my Web API project using Microsoft's tutorial.

Due to some requirement I would like to be able to retrieve a yaml file. But the problem is there is no hook I could find to make this possible.

Does anyone know any workaround for this problem?

maleu77
  • 32
  • 5
Manny42
  • 588
  • 2
  • 4
  • 21

3 Answers3

17

V 5.6 supports producing YAML files. How to use it:

app.UseSwaggerUI(x => { x.SwaggerEndpoint("/swagger/v1/swagger.yaml", "Zeipt Dashboard API"); });
VahidN
  • 18,457
  • 8
  • 73
  • 117
  • 1
    It works, but I have to remove `/swagger/`: `app.UseSwaggerUI(x => { x.SwaggerEndpoint("v1/swagger.yaml", "Zeipt Dashboard API"); });` – rgb Nov 08 '21 at 09:50
7

An option will be to add an IDocumentFilter to your project, here are a couple of samples:

    private class YamlDocumentFilter : IDocumentFilter
    {
        public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
        {
            string file = AppDomain.CurrentDomain.BaseDirectory + "swagger_yaml.txt";
            if (!File.Exists(file))
            {
                var serializer = new YamlSerializer();
                serializer.SerializeToFile(file, swaggerDoc);
            }
        }
    }

...

    private class YamlDocumentFilter : IDocumentFilter
    {
        public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
        {
            string file = AppDomain.CurrentDomain.BaseDirectory + "swagger.yaml";
            if (!File.Exists(file))
            {
                var serializer = new YamlDotNet.Serialization.Serializer();
                using (var writer = new StringWriter())
                {
                    serializer.Serialize(writer, swaggerDoc);
                    var stream = new StreamWriter(file);
                    stream.WriteLine(writer.ToString());
                }
            }
        }
    }

but that depends on your project if adding an additional reference to YamlSerializer or YamlDotNet is acceptable.

Helder Sepulveda
  • 15,500
  • 4
  • 29
  • 56
  • This seems to be the right way to go, I have not been able to verify it. But it might be because I am deploying to a service fabric. – Manny42 Jul 18 '17 at 10:33
  • arghhh! the devil in the details (***service fabric***) yes you probably need to modify that example or try with a different dependency... – Helder Sepulveda Jul 18 '17 at 13:22
  • I added a sample using YamlSerializer that code looks a lot simpler and I verified it that it works on Azure: http://swashbuckletest.azurewebsites.net/swagger_yaml.txt – Helder Sepulveda Jul 20 '17 at 15:02
  • 1
    @HelderSepu can you provide a more complete example, e.g. libraries, namespaces, usage etc. – Pawel Gorczynski Apr 05 '18 at 09:17
  • @PawełG. Take a look here: https://github.com/heldersepu/Swagger-Net-Test/blob/master/Swagger_Test/App_Start/SwaggerConfig.cs#L378 – Helder Sepulveda Apr 05 '18 at 15:13
2

Based on idea given by @HelderSepu I managed to get Swashbuckle.AspNetCore together with YamlDotNet generate the following YAML passing validation on https://bigstickcarpet.com/swagger-parser/www/index.html.

I know this solution is not ideal, but might be some starting point in case someone has the same problem:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.TypeInspectors;

namespace SwaggerPhun
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            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.AddMvc();

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new Info
                {
                    Version = "v1",
                    Title = "File Comment API",
                    Description = "A simple example ASP.NET Core Web API",
                    //TermsOfService = "None",
                    Contact = new Contact
                    {
                        Name = "Pawel",
                        Email = "pawel@example.com",
                    },
                    License = new License
                    {
                        Name = "Use under LICX",
                        Url = "https://example.com/license"
                    },
                });
                c.DocumentFilter<YamlDocumentFilter>();

                // Set the comments path for the Swagger JSON and UI.
                var xmlFile = $"{Assembly.GetEntryAssembly().GetName().Name}.xml";
                var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                c.IncludeXmlComments(xmlPath);
            });

        }

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

            app.UseMvc();

            // Enable middleware to serve generated Swagger as a JSON endpoint.
            app.UseSwagger();

            // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), specifying the Swagger JSON endpoint.
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
            });
        }

        private class YamlDocumentFilter : IDocumentFilter
        {
            public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
            {
                var builder = new SerializerBuilder();
                builder.WithNamingConvention(new HyphenatedNamingConvention());
                builder.WithTypeInspector(innerInspector => new PropertiesIgnoreTypeInspector(innerInspector));

                var serializer = builder.Build();

                using (var writer = new StringWriter())
                {
                    serializer.Serialize(writer, swaggerDoc);

                    var file = AppDomain.CurrentDomain.BaseDirectory + "swagger_yaml.txt";
                    using (var stream = new StreamWriter(file))
                    {
                        var result = writer.ToString();
                        stream.WriteLine(result.Replace("2.0", "\"2.0\"").Replace("ref:", "$ref:"));
                    }
                }
            }
        }

        private class PropertiesIgnoreTypeInspector : TypeInspectorSkeleton
        {
            private readonly ITypeInspector _typeInspector;

            public PropertiesIgnoreTypeInspector(ITypeInspector typeInspector)
            {
                this._typeInspector = typeInspector;
            }

            public override IEnumerable<IPropertyDescriptor> GetProperties(Type type, object container)
            {
                return _typeInspector.GetProperties(type, container).Where(p => p.Name != "extensions" && p.Name != "operation-id");
            }

        }
    }
}
Pawel Gorczynski
  • 1,227
  • 1
  • 15
  • 17
  • Since I had a bit of time, I took the code and crafted a library stub that mimics the original Swashbuckle but adds a `yaml` endpoint - the source can be found in: https://github.com/pablonautic/Swashbuckle.AspNetCore.Yaml – Pawel Gorczynski Jun 08 '18 at 07:16
  • Can we save the swagger.json file to local by this method? – Hello May 26 '20 at 14:34