Last active
June 3, 2017 00:35
-
-
Save BryanWilhite/1a0e8c14a5002995aa5eb7984bfa5cd0 to your computer and use it in GitHub Desktop.
Swashbucke IOperationFilter Example for XML Consumption/Production
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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