Skip to content

Instantly share code, notes, and snippets.

@BryanWilhite
Last active June 3, 2017 00:35
Show Gist options
  • Save BryanWilhite/1a0e8c14a5002995aa5eb7984bfa5cd0 to your computer and use it in GitHub Desktop.
Save BryanWilhite/1a0e8c14a5002995aa5eb7984bfa5cd0 to your computer and use it in GitHub Desktop.
Swashbucke IOperationFilter Example for XML Consumption/Production
namespace Songhay.Models
{
/// <summary>
/// Selected MIME types
/// </summary>
public static class MimeTypes
{
/// <summary>
/// The application atom XML
/// </summary>
public const string ApplicationAtomXml = "application/atom+xml";
/// <summary>
/// The application json
/// </summary>
public const string ApplicationJson = "application/json";
/// <summary>
/// The application octet stream
/// </summary>
public const string ApplicationOctetStream = "application/octet-stream";
/// <summary>
/// The application RSS XML
/// </summary>
public const string ApplicationRssXml = "application/rss+xml";
/// <summary>
/// The application VND ms font object
/// </summary>
public const string ApplicationVndMsFontObject = "application/vnd.ms-fontobject";
/// <summary>
/// The application x font otf
/// </summary>
public const string ApplicationXFontOtf = "application/x-font-otf";
/// <summary>
/// The application x font TTF
/// </summary>
public const string ApplicationXFontTtf = "application/x-font-ttf";
/// <summary>
/// The application x font woff
/// </summary>
public const string ApplicationXFontWoff = "application/x-font-woff";
/// <summary>
/// The application XML
/// </summary>
public const string ApplicationXml = "application/xml";
/// <summary>
/// The image GIF
/// </summary>
public const string ImageGif = "image/gif";
/// <summary>
/// The image JPEG
/// </summary>
public const string ImageJpeg = "image/jpeg";
/// <summary>
/// The image PNG
/// </summary>
public const string ImagePng = "image/png";
/// <summary>
/// The image SVG XML
/// </summary>
public const string ImageSvgXml = "image/svg+xml";
/// <summary>
/// The image x icon
/// </summary>
public const string ImageXIcon = "image/x-icon";
/// <summary>
/// The text CSS
/// </summary>
public const string TextCss = "text/css";
/// <summary>
/// The text HTML
/// </summary>
public const string TextHtml = "text/html";
/// <summary>
/// The text javascript
/// </summary>
public const string TextJavascript = "text/javascript";
/// <summary>
/// The text plain
/// </summary>
public const string TextPlain = "text/plain";
}
}
using System;
namespace Songhay.Services.Models
{
/// <summary>
/// Swashbuckle/Swagger Controller Attribute,
/// adding to <c>produces</c> fixed field.
/// </summary>
/// <remarks>
/// Example:
/// <code>
/// [ProductionContentType(mimeType:"application/pdf", Exclusive=true)]
/// </code>
///
/// The naming of this class refers to <c>produces</c> fixed field
/// in the Swagger Specification [http://swagger.io/specification/].
///
/// For detail, see “Swashbuckle Swagger - How to annotate content types?”
/// [http://stackoverflow.com/a/36348869/22944]
///
/// 'Exclusive=true' will remove all other content types,
/// otherwise using the new Attribute will add
/// a new Response Content Type in the Swagger UI drop down.
/// It will NOT modify your Controller or API just the documentation.
/// </remarks>
/// <seealso cref="Attribute" />
/// <seealso cref="ProductionContentTypeOperationFilter"/>
[AttributeUsage(AttributeTargets.Method)]
public sealed class ProductionContentTypeAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="ProductionContentTypeAttribute" /> class.
/// </summary>
/// <param name="mimeType">Type of the MIME.</param>
public ProductionContentTypeAttribute(string mimeType)
{
this.MimeType = mimeType;
}
/// <summary>
/// When <c>true</c> remove all other production mime types.
/// </summary>
public bool Exclusive { get; set; }
/// <summary>
/// Gets the type of the MIME.
/// </summary>
/// <value>
/// The type of the MIME.
/// </value>
public string MimeType { get; private set; }
}
}
namespace Songhay.GenericWeb.Swagger.Models
{
/// <summary>
/// Defines magic strings from the Swagger specification
/// [http://swagger.io/specification/]
/// </summary>
public static class SwaggerConstants
{
/// <summary>
/// The schema type array
/// </summary>
public const string SchemaTypeArray = "array";
/// <summary>
/// The schema type boolean
/// </summary>
public const string SchemaTypeBoolean = "boolean";
/// <summary>
/// The schema type integer
/// </summary>
public const string SchemaTypeInteger = "integer";
/// <summary>
/// The schema type number
/// </summary>
public const string SchemaTypeNumber = "number";
/// <summary>
/// The schema type object
/// </summary>
public const string SchemaTypeObject = "object";
/// <summary>
/// The schema type string
/// </summary>
public const string SchemaTypeString = "string";
}
}
using System;
namespace Songhay.GenericWeb.Swagger.Models
{
/// <summary>
/// Swashbuckle/Swagger Controller Attribute,
/// adding to <c>consumes</c> fixed field.
/// </summary>
/// <remarks>
/// Example:
/// <code>
/// [SwaggerContentTypeAttribute(inputType:"application/json", Exclusive=true)]
/// </code>
///
/// The naming of this class refers to <c>consumes</c> fixed field
/// in the Swagger Specification [http://swagger.io/specification/].
///
/// For detail, see “Swashbuckle Swagger - How to annotate content types?”
/// [http://stackoverflow.com/a/36348869/22944]
///
/// 'Exclusive=true' will remove all other content types,
/// otherwise using the new Attribute will add
/// a new Response Content Type in the Swagger UI drop down.
/// It will NOT modify your Controller or API just the documentation.
/// </remarks>
/// <seealso cref="Attribute" />
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public sealed class SwaggerContentTypeAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="ConsumptionContentTypeAttribute"/> class.
/// </summary>
/// <param name="mimeType">Type of the MIME.</param>
public SwaggerContentTypeAttribute(string mimeType)
{
this._attributeId = new object();
this.MimeType = mimeType;
}
/// <summary>
/// Gets or sets a value indicating whether this instance is declaring Swagger content consumption.
/// </summary>
/// <value>
/// <c>true</c> if this instance is consumption; otherwise, <c>false</c>.
/// </value>
public bool IsConsumption { get; set; }
/// <summary>
/// When <c>true</c> remove all other consumption/production mime types.
/// </summary>
public bool IsExclusive { get; set; }
/// <summary>
/// Gets or sets the name of the method parameter.
/// </summary>
/// <value>
/// The name of the method parameter.
/// </value>
public string MethodParameterName { get; set; }
/// <summary>
/// Input MIME type
/// </summary>
public string MimeType { get; private set; }
/// <summary>
/// Gets or sets the tag.
/// </summary>
/// <value>
/// The tag.
/// </value>
public string Tag { get; set; }
/// <summary>
/// When implemented in a derived class, gets a unique identifier for this <see cref="T:System.Attribute" />.
/// </summary>
public override object TypeId => this._attributeId;
readonly object _attributeId;
}
}
using Songhay.GenericWeb.Swagger.Extensions;
using Swashbuckle.Swagger;
using System.Linq;
using System.Web.Http.Description;
namespace Songhay.GenericWeb.Swagger.Models
{
public class SwaggerContentTypeOperationFilter : IOperationFilter
{
/// <summary>
/// Applies the specified operation.
/// </summary>
/// <param name="operation">The operation.</param>
/// <param name="schemaRegistry">The schema registry.</param>
/// <param name="apiDescription">The API description.</param>
/// <exception cref="System.NotImplementedException"></exception>
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
var swaggerAttribute = apiDescription.GetControllerAndActionAttributes<SwaggerContentTypeAttribute>().FirstOrDefault();
if (swaggerAttribute == null) return;
if (swaggerAttribute.IsConsumption)
{
if (operation.consumes == null) return;
if (swaggerAttribute.IsExclusive) operation.consumes.Clear();
operation.consumes.Add(swaggerAttribute.MimeType);
var swaggerParameter = operation.parameters.FirstOrDefault(i => i.name == swaggerAttribute.MethodParameterName);
if (swaggerParameter == null) return;
ApplyConsumption(swaggerAttribute, swaggerParameter);
}
else
{
if (operation.produces == null) return;
if (swaggerAttribute.IsExclusive) operation.produces.Clear();
operation.produces.Add(swaggerAttribute.MimeType);
operation.responses.Clear();
ApplyProduction(swaggerAttribute, operation);
}
}
static void ApplyConsumption(SwaggerContentTypeAttribute swaggerAttribute, Parameter swaggerParameter)
{
switch (swaggerAttribute.Tag)
{
case "ConsumeXml":
swaggerParameter.WitNullSchemaReference().WithAbbreviatedSchema();
break;
}
}
static void ApplyProduction(SwaggerContentTypeAttribute swaggerAttribute, Operation operation)
{
Response okResponse = null;
switch (swaggerAttribute.Tag)
{
case "ConsumeXml":
okResponse = new Response()
.WithOkDescription()
.WithAbbreviatedSchema();
break;
}
if (okResponse != null) operation.responses.Add(okResponse.To200Pair());
}
}
}
using Songhay.GenericWeb.Swagger.Models;
using Songhay.Models;
using Swashbuckle.Swagger;
using System;
using System.Collections.Generic;
namespace Songhay.GenericWeb.Swagger.Extensions
{
/// <summary>
/// Extensions of <see cref="Parameter"/>
/// </summary>
public static partial class SwashbuckleExtensions
{
/// <summary>
/// Sets the dictionary schema.
/// </summary>
/// <param name="schema">The schema.</param>
public static void SetDictionarySchema(this Schema schema)
{
if (schema == null) return;
var dictionarySchema = GetDictionarySchema();
schema.title = dictionarySchema.title;
schema.type = dictionarySchema.type;
schema.properties = dictionarySchema.properties;
}
/// <summary>
/// Sets summary items <see cref="Schema"/>.
/// </summary>
/// <param name="schema">The schema.</param>
public static void SetSummaryItemsSchema(this Schema schema)
{
if (schema == null) return;
schema.title = "summary-items";
schema.type = SwaggerConstants.SchemaTypeObject;
schema.properties = new Dictionary<string, Schema>
{
{ "key0", new Schema { type = SwaggerConstants.SchemaTypeArray, items = GetMenuDisplayItemModelSchema() } },
{ "key1", new Schema { type = SwaggerConstants.SchemaTypeArray, items = GetMenuDisplayItemModelSchema() } },
{ "key2", new Schema { type = SwaggerConstants.SchemaTypeArray, items = GetMenuDisplayItemModelSchema() } },
};
}
/// <summary>
/// Converts the <see cref="Response"/> to <see cref="KeyValuePair{TKey, TValue}"/>
/// for <c>200</c> response.
/// </summary>
/// <param name="response">The response.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentNullException">response</exception>
/// <exception cref="System.NullReferenceException">schema</exception>
public static KeyValuePair<string, Response> To200Pair(this Response response)
{
if (response == null) throw new ArgumentNullException($"The expected {nameof(response)} is not here.");
if (response.schema == null) throw new NullReferenceException($"The expected {nameof(response.schema)} is not here.");
return new KeyValuePair<string, Response>("200", response);
}
/// <summary>
/// Returns <see cref="Response"/>
/// with <see cref="Dictionary{TKey, TValue}"/> schema
/// where <c>TKey</c> is <see cref="string"/>
/// and <c>TValue</c> is <see cref="string"/>.
/// </summary>
/// <param name="response">The response.</param>
/// <returns></returns>
public static Response WithDictionarySchema(this Response response)
{
if (response == null) return null;
if (response.schema == null) response.schema = new Schema();
response.schema.SetDictionarySchema();
return response;
}
/// <summary>
/// Returns <see cref="Parameter"/>
/// with <c>Parameter.schema.@ref</c> set to null.
/// </summary>
/// <param name="parameter">The parameter.</param>
/// <returns></returns>
/// <remarks>
/// This evidently prevents Swashbuckle
/// from ignoring your changes to <see cref="Parameter"/>.
/// </remarks>
public static Parameter WitNullSchemaReference(this Parameter parameter)
{
if (parameter == null) return null;
if (parameter.schema == null) return null;
parameter.schema.@ref = null;
return parameter;
}
/// <summary>
/// Returns <see cref="Response"/>
/// with <see cref="Response.description"/> <c>= "OK"</c>.
/// </summary>
/// <param name="response">The response.</param>
/// <returns></returns>
public static Response WithOkDescription(this Response response)
{
if (response == null) return null;
if (response.schema == null) response.schema = new Schema();
response.description = "OK";
return response;
}
/// <summary>
/// Returns <see cref="Response"/>
/// with summary items schema.
/// </summary>
/// <param name="response">The response.</param>
/// <returns></returns>
public static Response WithSummaryItemsSchema(this Response response)
{
if (response == null) return null;
if (response.schema == null) response.schema = new Schema();
response.schema.SetSummaryItemsSchema();
return response;
}
static Schema GetDateStringSchema()
{
var stringSchema = new Schema
{
type = SwaggerConstants.SchemaTypeString,
example = DateTime.Now.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK")
};
return stringSchema;
}
static Schema GetBooleanSchema()
{
var boolSchema = new Schema
{
type = SwaggerConstants.SchemaTypeBoolean,
example = "true"
};
return boolSchema;
}
static Schema GetDictionarySchema()
{
return new Schema
{
title = "pairs",
properties = new Dictionary<string, Schema>
{
{ "key0", GetStringSchema() },
{ "key1", GetStringSchema() },
{ "key2", GetStringSchema() },
},
type = SwaggerConstants.SchemaTypeObject
};
}
static Schema GetIntegerSchema()
{
var intSchema = new Schema
{
type = SwaggerConstants.SchemaTypeInteger,
example = "0"
};
return intSchema;
}
static Schema GetMenuDisplayItemModelSchema()
{
return new Schema
{
properties = new Dictionary<string, Schema>
{
{ nameof(MenuDisplayItemModel.BackgroundHex), GetStringSchema() },
{ nameof(MenuDisplayItemModel.Description), GetStringSchema() },
{ nameof(MenuDisplayItemModel.DisplayText), GetStringSchema() },
{ nameof(MenuDisplayItemModel.ForegroundHex), GetStringSchema() },
{ nameof(MenuDisplayItemModel.Id), GetStringSchema() },
{ nameof(MenuDisplayItemModel.IsDefaultSelection), GetBooleanSchema() },
{ nameof(MenuDisplayItemModel.IsEnabled), GetBooleanSchema() },
{ nameof(MenuDisplayItemModel.IsSelected), GetBooleanSchema() },
{ nameof(MenuDisplayItemModel.ItemCategory), GetStringSchema() },
{ nameof(MenuDisplayItemModel.ItemName), GetStringSchema() },
{ nameof(MenuDisplayItemModel.ResourceIndicator), GetStringSchema() },
{ nameof(MenuDisplayItemModel.SortOrdinal), GetStringSchema() },
{ nameof(MenuDisplayItemModel.Tag), GetStringSchema() }
},
type = SwaggerConstants.SchemaTypeObject
};
}
static Schema GetStringSchema()
{
var stringSchema = new Schema
{
type = SwaggerConstants.SchemaTypeString,
example = "string"
};
return stringSchema;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment