Search code examples
swashbuckleasp.net-core-2.1swagger-codegen

Manually add custom route parameter to Swagger docs with Swashbuckle.AspNetCore


So I have encountered an interesting case, where I needed to generate swagger documentation for an REST API whose only documentation was an actual document (no inline XML documentation) and which I do not have direct source code access. So I simple wrote a wrapper controller, and overrid each route as such:

[HttpGet("this/{that}/the/{other}")]
public override IActionResult GetWhatever(String that, String other) => base.GetWhatever(that, other);

and then just documented it with standard summary tags, etc. However, one of the now overridden methods uses a querystring internally, and is not exposed as a parameter with [FromQuery], so it is not able to be auto-documented reflectively (and putting in a tag for it without the actual parameter present does not generate documentation for it)

I need a way to add this parameter documentation manually, but through code somehow (not just by adding it to the swagger.json file). I though I could use SwaggerGen's ISchemaFilter to add a parameter description to the associated route/method, but so far I'm not having much luck.

Does anyone have an example of doing something like this?


Solution

  • So it appears what I was looking for was IOpertationFilter. Coupling that with a custom Attribute, I was able to create what I needed to manually add a custom parameter to the Swagger documentation on the fly. See all associated code below, please note Schema/PartialSchema have many properties, I only set Type as it was all I needed, other cases might require more.

    SwaggerParameterAttribute.cs

    using System;
    using Microsoft.AspNetCore.Mvc.Filters;
    using Swashbuckle.AspNetCore.Swagger;
    
    /// <summary>
    /// Types of Swagger parameters
    /// </summary>
      public enum SwaggerParamType {Body, NonBody};
    
    /// <summary>
    /// Attribute to facilitate manually adding a parameter to auto-generated Swagger documentation
    /// </summary>
      [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
      public class SwaggerParameterAttribute : ActionFilterAttribute {
    
      /// <summary>
      /// Swagger parameter to inject
      /// </summary>
        public IParameter Parameter { get; set; } 
    
      /// <summary>
      /// Default constructor
      /// </summary>
      /// <param name="ParamType">Type of Swagger parameter (Body/NonBody)</param>
      /// <param name="Name">Name of the parameter</param>
      /// <param name="Type">Primitive type associated with the parameter (int, bool, string, etc.)</param>
      /// <param name="In">Location of the parameter (path, query, etc.)</param>
      /// <param name="Description">Description of the parameter</param>
      /// <param name="Required">Whether the parameter is required or not (true/false)</param>
        public SwaggerParameterAttribute(SwaggerParamType ParamType, String Name, String Type, String In, String Description = "", Boolean Required = false){
      switch (ParamType) {      
        case SwaggerParamType.Body:
          Parameter = new BodyParameter() { Name = Name, In = In, Description = Description, Required = Required, Schema = new Schema() { Type = Type } };
          break;    
        case SwaggerParamType.NonBody:
          Parameter = new NonBodyParameter() { Name = Name, In = In, Description = Description, Required = Required };  
          ((PartialSchema)Parameter).Type = Type;
          break;
        default:
          throw new ArgumentOutOfRangeException("Invalid Swagger parameter type specified.");
      }
    }
    

    SwaggerOperationFilter.cs

    using System;
    using System.Reflection;
    using Microsoft.AspNetCore.Mvc.Controllers;
    using Swashbuckle.AspNetCore.Swagger;
    using Swashbuckle.AspNetCore.SwaggerGen;
    
    using Whatever.NameSpace.Your.Attribute.Is.In;
    
    /// <summary>
    /// Custom Swagger Operation Filter
    /// </summary>
      public class SwaggerOperationFilter : IOperationFilter {
        public void Apply(Operation operation, OperationFilterContext context) {
        //Check for [SwaggerParameter] add defined parameter to the parameter list
          foreach (Attribute attribute in ((ControllerActionDescriptor)context.ControllerActionDescriptor).MethodInfo.GetCustomAttributes()) {
            if (attribute.GetType() == typeof(SwaggerParameterAttribute)) {
              operation.Parameters.Add(((SwaggerParameterAttribute)attribute).Parameter);
            }
          }
        }
      }
    

    Startup.cs (Just the swagger operation filter part)

    using Swashbuckle.AspNetCore.Swagger;
    
    using Whatever.NameSpace.Your.Filter.Is.In;
    
    public void ConfigureServices(IServiceCollection services) {
      services.AddSwaggerGen(options => {
        options.OperationFilter<SwaggerOperationFilter>();
      }
    }
    

    SomeController.cs (Example Usage)

    using Swashbuckle.AspNetCore.Swagger;
    using Swashbuckle.AspNetCore.SwaggerGen;
    
    using Whatever.NameSpace.Your.Attribute.Is.In;
    
    [HttpGet("this/{that}/the/{other}")]
    [SwaggerParameter(ParamType: SwaggerParamType.NonBody, Name: "param1", Type: "string", In: "query", Description: "Some description of param1 here")]
    [SwaggerParameter(SwaggerParamType.NonBody, "param2", "string", "query", "Some description of param2 here")]
    public override IActionResult GetWhatever(String that, String other) => base.GetWhatever(that, other);