├── icon.png ├── Tests ├── Swagger.Net.Dummy.Core │ ├── SwaggerExtensions │ │ ├── testScript2.js │ │ ├── testScript1.js │ │ ├── testStyles1.css │ │ ├── testStyles2.css │ │ ├── AddDescription.cs │ │ ├── DescendingAlphabeticComparer.cs │ │ ├── AddMessageDefault.cs │ │ ├── ApplyDocumentVendorExtensions.cs │ │ ├── InternalServerErrorResponseOperationFilter.cs │ │ ├── UpdateFileDownloadOperations.cs │ │ ├── RecursiveCallSchemaFilter.cs │ │ ├── AddDefaultResponse.cs │ │ ├── AddGetMessageExamples.cs │ │ ├── ApplyResponseVendorExtensions.cs │ │ ├── ApplySchemaVendorExtensions.cs │ │ ├── AddFileUploadParams.cs │ │ ├── SupportFlaggedEnums.cs │ │ ├── AssignOAuth2SecurityRequirements.cs │ │ └── AppendVersionToBasePath.cs │ ├── bad.xml │ ├── Types │ │ ├── PrimitiveEnum.cs │ │ └── Location.cs │ ├── Controllers │ │ ├── FileUploadController.cs │ │ ├── FromBodyParamsController.cs │ │ ├── RangeAttribController.cs │ │ ├── ProtectedResources2Controller.cs │ │ ├── BaseChildController.cs │ │ ├── NullableTypesController.cs │ │ ├── FromHeaderParamsController.cs │ │ ├── ProtectedResources3Controller.cs │ │ ├── SerializableController.cs │ │ ├── ObsoleteEnumsController.cs │ │ ├── DictionaryTypesController.cs │ │ ├── TwoDimensionalArraysController.cs │ │ ├── ConflictingActionsController.cs │ │ ├── ObsoletePropertiesController.cs │ │ ├── AttributeRoutesController.cs │ │ ├── NestedTypesController.cs │ │ ├── MultipleApiVersionsController.cs │ │ ├── NestedEnumController.cs │ │ ├── IndexerTypesController.cs │ │ ├── OverloadedAttributeRoutesController.cs │ │ ├── NestingClassController.cs │ │ ├── ArraysTestController.cs │ │ ├── BlobController.cs │ │ ├── ConflictingTypesController.cs │ │ ├── PolymorphicTypesController.cs │ │ ├── JsonAnnotatedTypesController.cs │ │ ├── CustomersController.cs │ │ ├── PathRequiredController.cs │ │ ├── DataContractAnnotatedTypesController.cs │ │ ├── ProtectedResourcesController.cs │ │ ├── SelfReferencingTypesController.cs │ │ ├── BaseController.cs │ │ ├── TestBindingController.cs │ │ ├── ObsoleteActionsController.cs │ │ ├── ProductsController.cs │ │ ├── FileDownloadController.cs │ │ ├── DynamicTypesController.cs │ │ ├── AnnotatedTypesController.cs │ │ ├── FromUriParamsController.cs │ │ ├── MetadataAnnotatedTypesController.cs │ │ └── SwaggerAnnotatedController.cs │ ├── packages.config │ ├── App_Start │ │ ├── CachingSwaggerProvider.cs │ │ └── WebApiConfig.cs │ ├── app.config │ └── Properties │ │ └── AssemblyInfo.cs ├── Swagger.Net.Dummy.WebHost │ ├── Global.asax │ ├── Global.asax.cs │ ├── packages.config │ ├── Web.Debug.config │ ├── Web.Release.config │ ├── Properties │ │ └── AssemblyInfo.cs │ └── Web.config ├── Swagger.Net.Tests │ ├── Extensions.cs │ ├── CoreUnitTests │ │ ├── ModelFilterContextTests.cs │ │ ├── VendorExtensionsConverterTest.cs │ │ ├── StringExtensionsTests.cs │ │ ├── HttpRouteDirectionConstraintTests.cs │ │ ├── RedirectHandlerTests.cs │ │ ├── HandleFromUriParamsTests.cs │ │ ├── ApplySwaggerResponseAttributesTests.cs │ │ ├── ApplyXmlTypeCommentsTests.cs │ │ ├── SwaggerDocsConfigTests.cs │ │ ├── JsonPropertyExtensionsTests.cs │ │ ├── ApplySwaggerOperationAttributesTests.cs │ │ ├── SwaggerEnabledConfigurationTests.cs │ │ ├── SchemaExtensionsTests.cs │ │ ├── ApplyXmlActionCommentsTests.cs │ │ ├── ApiDescriptionExtensionsTests.cs │ │ ├── XPathNavigatorExtensionsTests.cs │ │ ├── XmlCommentsIdHelperTests.cs │ │ └── EmbeddedAssetProviderTests.cs │ ├── Owin │ │ ├── MultiSwaggerOwinStartup.cs │ │ ├── PredefinedHttpControllerSelector.cs │ │ ├── InMemoryOwinTest.cs │ │ ├── BaseInMemoryOwinSwaggerTest.cs │ │ └── OwinStartup.cs │ ├── Swagger │ │ ├── SwaggerTestBase.cs │ │ ├── SerializableTests.cs │ │ ├── SchemaTestsWithCustomAuthorizeAttribute.cs │ │ ├── XmlComments2Tests.cs │ │ ├── FromHeaderParamsTests.cs │ │ ├── SchemaArraysTests.cs │ │ ├── SecurityTests2.cs │ │ ├── SecurityTests3.cs │ │ ├── DefaultRootUrlResolverTests.cs │ │ └── TypeExtensionsTests.cs │ ├── packages.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── app.config │ └── HttpMessageHandlerTestBase.cs └── Swagger.Net.Dummy.SelfHost │ ├── packages.config │ ├── Program.cs │ ├── app.config │ ├── Properties │ └── AssemblyInfo.cs │ └── Swagger.Net.Dummy.SelfHost.csproj ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE.md └── workflows │ └── build.yml ├── Swagger.Net ├── Swagger-Net.snk ├── Swagger │ ├── ISchemaFilter.cs │ ├── IDocumentFilter.cs │ ├── IOperationFilter.cs │ ├── Annotations │ │ ├── SwaggerResponseRemoveDefaultsAttribute.cs │ │ ├── SwaggerSchemaFilterAttribute.cs │ │ ├── SwaggerOperationFilterAttribute.cs │ │ ├── SwaggerOperationAttribute.cs │ │ ├── SwaggerDescriptionAttribute.cs │ │ ├── ApplySwaggerOperationFilterAttributes.cs │ │ ├── ApplySwaggerOperationAttributes.cs │ │ ├── ApplySwaggerSchemaFilterAttributes.cs │ │ ├── SwaggerExampleAttribute.cs │ │ ├── ApplySwaggerExampleAttribute.cs │ │ ├── SwaggerResponseAttribute.cs │ │ ├── ApplySwaggerOperationApiKey.cs │ │ └── ApplySwaggerResponseAttributes.cs │ ├── ISwaggerProvider.cs │ ├── Comparers │ │ └── TagNameEqualityComparer.cs │ ├── StringExtensions.cs │ ├── IModelFilter.cs │ ├── Extensions │ │ ├── ApiParameterDescriptionExtensions.cs │ │ ├── JsonContractExtensions.cs │ │ ├── JsonPropertyExtensions.cs │ │ └── TypeExtensions.cs │ ├── XmlComments │ │ ├── XPathNavigatorExtensions.cs │ │ ├── XmlTextHelper.cs │ │ └── ApplyXmlTypeComments.cs │ └── FromUriParams │ │ └── HandleFromUriParams.cs ├── packages.config ├── app.config ├── SwaggerUi │ ├── CustomAssets │ │ └── typography.css │ ├── EmbeddedAssetDescriptor.cs │ ├── IAssetProvider.cs │ ├── StreamExtensions.cs │ └── EmbeddedAssetProvider.cs ├── Application │ ├── LicenseBuilder.cs │ ├── HttpRouteDirectionConstraint.cs │ ├── VersionInfoBuilder.cs │ ├── ContactBuilder.cs │ ├── RedirectHandler.cs │ ├── InfoBuilder.cs │ ├── VendorExtensionsConverter.cs │ ├── SwaggerUiHandler.cs │ ├── SecuritySchemeBuilder.cs │ └── HttpConfigurationExtensions.cs ├── Properties │ └── AssemblyInfo.cs ├── Nuget │ └── Tools │ │ └── Init.ps1 └── Swagger.Net.nuspec ├── .gitmodules ├── UpdateSubModules.sh ├── LICENSE.TXT ├── README.md └── Swagger.Net.sln /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heldersepu/Swagger-Net/HEAD/icon.png -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/testScript2.js: -------------------------------------------------------------------------------- 1 | var str2 = 'foobar'; -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [heldersepu] 4 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/testScript1.js: -------------------------------------------------------------------------------- 1 | var str1 = "testScript1"; 2 | alert(str1); -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/testStyles1.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | background-color: #FF0000; 3 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/testStyles2.css: -------------------------------------------------------------------------------- 1 | h2 { 2 | background-color: #00FF00; 3 | } -------------------------------------------------------------------------------- /Swagger.Net/Swagger-Net.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heldersepu/Swagger-Net/HEAD/Swagger.Net/Swagger-Net.snk -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/bad.xml: -------------------------------------------------------------------------------- 1 | // This is not xml format 2 | 3 | namespace Swagger.Net.Dummy 4 | { 5 | class bad 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.WebHost/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="Swagger.Net.Dummy.WebHost.WebApiApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Types/PrimitiveEnum.cs: -------------------------------------------------------------------------------- 1 | namespace Swagger.Net.Dummy.Types 2 | { 3 | public enum PrimitiveEnum 4 | { 5 | OneFish, TwoFish, RedFish, BlueFish 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Swagger.Net/Swagger/ISchemaFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Swagger.Net 4 | { 5 | public interface ISchemaFilter 6 | { 7 | void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type); 8 | } 9 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Types/Location.cs: -------------------------------------------------------------------------------- 1 | namespace Swagger.Net.Dummy.Types 2 | { 3 | public class Location 4 | { 5 | public decimal Lat { get; set; } 6 | public decimal Lon { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "swagger-ui"] 2 | path = swagger-ui 3 | url = https://github.com/swagger-api/swagger-ui.git 4 | [submodule "swagger-ui-themes"] 5 | path = swagger-ui-themes 6 | url = https://github.com/ostranme/swagger-ui-themes 7 | -------------------------------------------------------------------------------- /UpdateSubModules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Update all SubModules to latest and commit 3 | 4 | cd swagger-ui 5 | git checkout master 6 | git pull 7 | cd .. 8 | 9 | cd swagger-ui-themes 10 | git checkout develop 11 | git pull 12 | cd .. 13 | 14 | git commit -am "Update Swagger-UI" 15 | -------------------------------------------------------------------------------- /Swagger.Net/Swagger/IDocumentFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http.Description; 2 | 3 | namespace Swagger.Net 4 | { 5 | public interface IDocumentFilter 6 | { 7 | void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Swagger.Net/Swagger/IOperationFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http.Description; 2 | 3 | namespace Swagger.Net 4 | { 5 | public interface IOperationFilter 6 | { 7 | void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Annotations/SwaggerResponseRemoveDefaultsAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Swagger.Net.Annotations 4 | { 5 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] 6 | public class SwaggerResponseRemoveDefaultsAttribute : Attribute 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/FileUploadController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.Controllers 5 | { 6 | public class FileUploadController : ApiController 7 | { 8 | public int PostFormData() 9 | { 10 | throw new NotImplementedException(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/FromBodyParamsController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | 3 | namespace Swagger.Net.Dummy.Controllers 4 | { 5 | public class FromBodyParamsController : ApiController 6 | { 7 | [HttpGet] 8 | public Transaction Get([FromBody] Transaction t) 9 | { 10 | return t; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/AddDescription.cs: -------------------------------------------------------------------------------- 1 | namespace Swagger.Net.Dummy.SwaggerExtensions 2 | { 3 | public class AddDescription : ISchemaFilter 4 | { 5 | public void Apply(Schema schema, SchemaRegistry schemaRegistry, System.Type type) 6 | { 7 | schema.description = "Some description"; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Extensions.cs: -------------------------------------------------------------------------------- 1 | namespace Swagger.Net.Tests 2 | { 3 | public static class StringExtensions 4 | { 5 | public static string Strip(this string value) 6 | { 7 | if (string.IsNullOrEmpty(value)) return value; 8 | return value.Replace("\n", "").Replace("\r", "").Replace(" ", ""); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/DescendingAlphabeticComparer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Swagger.Net.Dummy.SwaggerExtensions 4 | { 5 | public class DescendingAlphabeticComparer : IComparer 6 | { 7 | public int Compare(string x, string y) 8 | { 9 | return y.CompareTo(x); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/RangeAttribController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Web.Http; 4 | 5 | namespace Swagger.Net.Dummy.Controllers 6 | { 7 | public class RangeAttribController : ApiController 8 | { 9 | public int Get([Range(1, 9)] int id) 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/AddMessageDefault.cs: -------------------------------------------------------------------------------- 1 | namespace Swagger.Net.Dummy.SwaggerExtensions 2 | { 3 | public class AddMessageDefault : ISchemaFilter 4 | { 5 | public void Apply(Schema schema, SchemaRegistry schemaRegistry, System.Type type) 6 | { 7 | schema.example = schema.@default = new { title = "A message", content = "Some content" }; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Swagger.Net/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.WebHost/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.WebHost 5 | { 6 | public class WebApiApplication : HttpApplication 7 | { 8 | protected void Application_Start() 9 | { 10 | SwaggerConfig.Register(GlobalConfiguration.Configuration); 11 | GlobalConfiguration.Configure(WebApiConfig.Register); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/ModelFilterContextTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace Swagger.Net.Tests.CoreUnitTests 4 | { 5 | [TestFixture] 6 | class ModelFilterContextTests 7 | { 8 | [Test] 9 | public void SchemaRegistry_Null() 10 | { 11 | var x = new ModelFilterContext(null, null, null); 12 | Assert.IsNull(x.SchemaRegistry); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Swagger.Net/Swagger/ISwaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Swagger.Net 4 | { 5 | public interface ISwaggerProvider 6 | { 7 | SwaggerDocument GetSwagger(string rootUrl, string apiVersion); 8 | } 9 | 10 | public class UnknownApiVersion : Exception 11 | { 12 | public UnknownApiVersion(string apiVersion) 13 | : base(String.Format("Unknown API version - {0}", apiVersion)) 14 | {} 15 | } 16 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.SelfHost/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Comparers/TagNameEqualityComparer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Swagger.Net 4 | { 5 | public class TagNameEqualityComparer : IEqualityComparer 6 | { 7 | public bool Equals(Tag x, Tag y) 8 | { 9 | return x.name.Equals(y.name); 10 | } 11 | 12 | public int GetHashCode(Tag obj) 13 | { 14 | return obj.name.GetHashCode(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Swagger.Net/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/ProtectedResources2Controller.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.Controllers 5 | { 6 | public class ProtectedResources2Controller : ApiController 7 | { 8 | [TokenAuth] 9 | public string Get() 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | } 14 | 15 | public class TokenAuthAttribute : Attribute 16 | { 17 | } 18 | } -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Annotations/SwaggerSchemaFilterAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Swagger.Net.Annotations 4 | { 5 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 6 | public class SwaggerSchemaFilterAttribute : Attribute 7 | { 8 | public SwaggerSchemaFilterAttribute(Type filterType) 9 | { 10 | FilterType = filterType; 11 | } 12 | 13 | public Type FilterType { get; private set; } 14 | } 15 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/BaseChildController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.Controllers 5 | { 6 | public class BaseChildController : BaseController 7 | { 8 | /// 9 | /// Get some ... 10 | /// 11 | public IEnumerable Get() 12 | { 13 | return new string[] { "aei", "bcd" }; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/ApplyDocumentVendorExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http.Description; 2 | 3 | namespace Swagger.Net.Dummy.SwaggerExtensions 4 | { 5 | public class ApplyDocumentVendorExtensions : IDocumentFilter 6 | { 7 | public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer) 8 | { 9 | swaggerDoc.vendorExtensions.Add("x-document", "foo"); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Annotations/SwaggerOperationFilterAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Swagger.Net.Annotations 4 | { 5 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] 6 | public class SwaggerOperationFilterAttribute : Attribute 7 | { 8 | public SwaggerOperationFilterAttribute(Type filterType) 9 | { 10 | FilterType = filterType; 11 | } 12 | 13 | public Type FilterType { get; private set; } 14 | } 15 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/NullableTypesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.Controllers 5 | { 6 | public class NullableTypesController : ApiController 7 | { 8 | public int Create(Contact contact) 9 | { 10 | throw new NotImplementedException(); 11 | } 12 | } 13 | 14 | public class Contact 15 | { 16 | public string Name { get; set; } 17 | 18 | public int? Phone { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/FromHeaderParamsController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | 3 | namespace Swagger.Net.Dummy.Controllers 4 | { 5 | public class FromHeaderParamsController : ApiController 6 | { 7 | [HttpGet] 8 | public string Get([FromHeader] string test) 9 | { 10 | return test; 11 | } 12 | 13 | [HttpPost] 14 | public string Post([FromHeader(Name = "abctest")] string test) 15 | { 16 | return test; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/ProtectedResources3Controller.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.Controllers 5 | { 6 | [TokenClassAuth] 7 | public class ProtectedResources3Controller : ApiController 8 | { 9 | public string Get() 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | } 14 | 15 | [AttributeUsage(AttributeTargets.Class)] 16 | public class TokenClassAuthAttribute : Attribute 17 | { 18 | } 19 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/InternalServerErrorResponseOperationFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http.Description; 2 | 3 | namespace Swagger.Net.Dummy.SwaggerExtensions 4 | { 5 | public class InternalServerErrorResponseOperationFilter : IOperationFilter 6 | { 7 | public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) 8 | { 9 | operation.responses["500"] = new Response { description = "Internal server error" }; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.WebHost/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/SerializableController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.Controllers 5 | { 6 | public class SerializableController : ApiController 7 | { 8 | public CountingLock Get([FromUri] CountingLock cl) 9 | { 10 | return cl; 11 | } 12 | } 13 | 14 | [Serializable] 15 | public class CountingLock 16 | { 17 | public int EmployeeID { get; set; } 18 | public string EmployeeName { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/VendorExtensionsConverterTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Swagger.Net.Application; 3 | using System; 4 | 5 | namespace Swagger.Net.Tests.CoreUnitTests 6 | { 7 | [TestFixture] 8 | class VendorExtensionsConverterTests 9 | { 10 | [Test] 11 | public void ReadJson_Test() 12 | { 13 | var obj = new VendorExtensionsConverter(); 14 | Assert.Throws(() => obj.ReadJson(null, null, null, null)); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Swagger.Net/SwaggerUi/CustomAssets/typography.css: -------------------------------------------------------------------------------- 1 | /* Google Font's Droid Sans */ 2 | @font-face { 3 | font-family: 'Droid Sans'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: local('Droid Sans'), local('DroidSans'), url('../fonts/DroidSans-ttf'), format('truetype'); 7 | } 8 | /* Google Font's Droid Sans Bold */ 9 | @font-face { 10 | font-family: 'Droid Sans'; 11 | font-style: normal; 12 | font-weight: 700; 13 | src: local('Droid Sans Bold'), local('DroidSans-Bold'), url('../fonts/DroidSans-Bold-ttf'), format('truetype'); 14 | } 15 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/ObsoleteEnumsController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.Controllers 5 | { 6 | public class ObsoleteEnumsController : ApiController 7 | { 8 | public int Get(Colors color) 9 | { 10 | throw new NotImplementedException(); 11 | } 12 | } 13 | 14 | public enum Colors 15 | { 16 | [Obsolete] 17 | Red = 0, 18 | 19 | [Obsolete] 20 | Blue = 1, 21 | 22 | Green = 2 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/DictionaryTypesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Web.Http; 4 | 5 | namespace Swagger.Net.Dummy.Controllers 6 | { 7 | public class DictionaryTypesController : ApiController 8 | { 9 | [Route("term-definitions")] 10 | public IDictionary GetTermDescriptions() 11 | { 12 | throw new NotImplementedException(); 13 | } 14 | } 15 | 16 | public enum Term 17 | { 18 | TermA, 19 | TermB 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/TwoDimensionalArraysController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Web.Http; 4 | 5 | namespace Swagger.Net.Dummy.Controllers 6 | { 7 | public class TwoDimensionalArraysController : ApiController 8 | { 9 | public int Get([FromUri] HashSet matrix) 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | 14 | public int Create(int[][] matrix) 15 | { 16 | throw new NotImplementedException(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/ConflictingActionsController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Web.Http; 4 | 5 | namespace Swagger.Net.Dummy.Controllers 6 | { 7 | public class ConflictingActionsController : ApiController 8 | { 9 | public IEnumerable GetAll() 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | 14 | public IEnumerable GetAllByKeyword(string keyword) 15 | { 16 | throw new NotImplementedException(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Annotations/SwaggerOperationAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Swagger.Net.Annotations 4 | { 5 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] 6 | public class SwaggerOperationAttribute : Attribute 7 | { 8 | public SwaggerOperationAttribute(string operationId = null) 9 | { 10 | OperationId = operationId; 11 | } 12 | 13 | public string OperationId { get; set; } 14 | 15 | public string[] Tags { get; set; } 16 | 17 | public string[] Schemes { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/UpdateFileDownloadOperations.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http.Description; 2 | 3 | namespace Swagger.Net.Dummy.SwaggerExtensions 4 | { 5 | public class UpdateFileDownloadOperations : IOperationFilter 6 | { 7 | public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) 8 | { 9 | if (operation.operationId == "FileDownload_GetFile") 10 | { 11 | operation.produces = new[] { "application/octet-stream" }; 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/StringExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Swagger.Net.Swagger; 3 | 4 | namespace Swagger.Net.Tests.CoreUnitTests 5 | { 6 | [TestFixture] 7 | class StringExtensionsTests 8 | { 9 | [Test] 10 | public void ToCamelCase_null() 11 | { 12 | Assert.AreEqual(string.Empty, string.Empty.ToCamelCase()); 13 | } 14 | 15 | [Test] 16 | public void ToTitleCase_null() 17 | { 18 | Assert.AreEqual(string.Empty, string.Empty.ToTitleCase()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: 'build' 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | runs-on: windows-latest 12 | 13 | steps: 14 | - uses: actions/checkout@master 15 | 16 | - name: Add msbuild to PATH 17 | uses: microsoft/setup-msbuild@v1.1 18 | 19 | - name: Versions 20 | run: | 21 | msbuild -version 22 | reg query "HKLM\SOFTWARE\Microsoft\Net Framework Setup\NDP" /s 23 | 24 | - name: MSBuild 25 | if: github.ref == 'disable for now' 26 | run: msbuild Swagger.Net.sln 27 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/RecursiveCallSchemaFilter.cs: -------------------------------------------------------------------------------- 1 | using Swagger.Net.Dummy.Controllers; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace Swagger.Net.Dummy.SwaggerExtensions 6 | { 7 | public class RecursiveCallSchemaFilter : ISchemaFilter 8 | { 9 | public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type) 10 | { 11 | schema.properties = new Dictionary(); 12 | schema.properties.Add("ExtraProperty", schemaRegistry.GetOrRegister(typeof(Product))); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Swagger.Net/Swagger/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Swagger.Net 2 | { 3 | public static class StringExtensions 4 | { 5 | public static string ToCamelCase(this string value) 6 | { 7 | if (string.IsNullOrEmpty(value)) return value; 8 | return value.Substring(0, 1).ToLowerInvariant() + value.Substring(1); 9 | } 10 | 11 | public static string ToTitleCase(this string value) 12 | { 13 | if (string.IsNullOrEmpty(value)) return value; 14 | return char.ToUpperInvariant(value[0]) + value.Substring(1); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Swagger.Net/SwaggerUi/EmbeddedAssetDescriptor.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace Swagger.Net.SwaggerUi 4 | { 5 | public class EmbeddedAssetDescriptor 6 | { 7 | public EmbeddedAssetDescriptor(Assembly containingAssembly, string name, bool isTemplate) 8 | { 9 | Assembly = containingAssembly; 10 | Name = name; 11 | IsTemplate = isTemplate; 12 | } 13 | 14 | public Assembly Assembly { get; private set; } 15 | 16 | public string Name { get; private set; } 17 | 18 | public bool IsTemplate { get; private set; } 19 | } 20 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/ObsoletePropertiesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.Controllers 5 | { 6 | public class ObsoletePropertiesController : ApiController 7 | { 8 | public int Create(Event @event) 9 | { 10 | throw new NotImplementedException(); 11 | } 12 | } 13 | 14 | public class Event 15 | { 16 | [Obsolete("Switched to generated ids - consumers should only provide Name")] 17 | public int Id { get; set; } 18 | 19 | public string Name { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Annotations/SwaggerDescriptionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Swagger.Net.Swagger.Annotations 8 | { 9 | public class SwaggerDescriptionAttribute: Attribute 10 | { 11 | public SwaggerDescriptionAttribute(string description = null, string summary = null) 12 | { 13 | Description = description; 14 | Summary = summary; 15 | } 16 | 17 | public string Description { get; set; } 18 | public string Summary { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/AttributeRoutesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.Controllers 5 | { 6 | public class AttributeRoutesController : ApiController 7 | { 8 | [Route("subscriptions/{id}/cancel")] 9 | public void CancelSubscription(int id) 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | 14 | [Route("post/one")] 15 | [Route("post/two")] 16 | [Route("post/abc")] 17 | public void Post(int id) 18 | { 19 | throw new NotImplementedException(); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/HttpRouteDirectionConstraintTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Swagger.Net.Application; 3 | using System.Web.Http.Routing; 4 | 5 | namespace Swagger.Net.Tests.CoreUnitTests 6 | { 7 | [TestFixture] 8 | class HttpRouteDirectionConstraintTests 9 | { 10 | [Test] 11 | public void It_returns_match() 12 | { 13 | HttpRouteDirection direction = HttpRouteDirection.UriResolution; 14 | HttpRouteDirectionConstraint route = new HttpRouteDirectionConstraint(direction); 15 | Assert.IsTrue(route.Match(null,null,"", null, direction)); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/NestedTypesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Web.Http; 4 | 5 | namespace Swagger.Net.Dummy.Controllers 6 | { 7 | public class NestedTypesController : ApiController 8 | { 9 | public int Create(Order order) 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | } 14 | 15 | public class Order 16 | { 17 | public IEnumerable LineItems { get; set; } 18 | } 19 | 20 | public class LineItem 21 | { 22 | public int ProductId { get; set; } 23 | public int Quantity { get; set; } 24 | } 25 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/RedirectHandlerTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Swagger.Net.Application; 3 | using System.Net.Http; 4 | 5 | namespace Swagger.Net.Tests.CoreUnitTests 6 | { 7 | [TestFixture] 8 | class RedirectHandlerTests 9 | { 10 | internal string GetRootUrl(HttpRequestMessage r) 11 | { 12 | return "http://tempuri.org"; 13 | } 14 | 15 | [Test] 16 | public void SendAsync_null() 17 | { 18 | var redirect = new RedirectHandler(GetRootUrl, "swagger"); 19 | Assert.DoesNotThrow(() => redirect.SendAsync(new HttpRequestMessage())); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/MultipleApiVersionsController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.Controllers 5 | { 6 | public class MultipleApiVersionsController : ApiController 7 | { 8 | [Route("{apiVersion:regex(v1|v2)}/todos")] 9 | public int Create([FromBody]string description) 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | 14 | [HttpPut] 15 | [Route("{apiVersion:regex(v2)}/todos/{id}")] 16 | public void Update(int id, [FromBody]string description) 17 | { 18 | throw new NotImplementedException(); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/NestedEnumController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | 3 | namespace Swagger.Net.Dummy.Controllers 4 | { 5 | public abstract class NestedEnum : ApiController 6 | { 7 | public enum Giorno { Lunedi, Martedi, Venerdi } 8 | 9 | /// NestedEnum test 10 | public string Get(Giorno? value) 11 | { 12 | return "Enum parameter"; 13 | } 14 | 15 | /// NestedEnum test 16 | public string Put(Giorno value) 17 | { 18 | return "Nullable Enum parameter"; 19 | } 20 | } 21 | 22 | public class NestedEnumController : NestedEnum { } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/AddDefaultResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | using System.Web.Http.Description; 3 | 4 | namespace Swagger.Net.Dummy.SwaggerExtensions 5 | { 6 | public class AddDefaultResponse : IOperationFilter 7 | { 8 | public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) 9 | { 10 | var errorSchema = schemaRegistry.GetOrRegister(typeof(HttpError)); 11 | 12 | operation.responses.Add("default", new Response 13 | { 14 | description = "Error", 15 | schema = errorSchema 16 | }); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/AddGetMessageExamples.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Web.Http.Description; 3 | 4 | namespace Swagger.Net.Dummy.SwaggerExtensions 5 | { 6 | public class AddGetMessageExamples : IOperationFilter 7 | { 8 | public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) 9 | { 10 | var successResponse = operation.responses["200"]; 11 | successResponse.examples = new Dictionary() 12 | { 13 | { "application/json", new { title = "A message", content = "Some content" } } 14 | }; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.SelfHost/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http.SelfHost; 3 | 4 | namespace Swagger.Net.Dummy.SelfHost 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | var config = new HttpSelfHostConfiguration("http://localhost:8080"); 11 | 12 | SwaggerConfig.Register(config); 13 | WebApiConfig.Register(config); 14 | 15 | using (var server = new HttpSelfHostServer(config)) 16 | { 17 | server.OpenAsync().Wait(); 18 | Console.WriteLine("Press Enter to quit."); 19 | Console.ReadLine(); 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/ApplyResponseVendorExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Web.Http.Description; 3 | 4 | namespace Swagger.Net.Dummy.SwaggerExtensions 5 | { 6 | public class ApplyResponseVendorExtensions : IOperationFilter 7 | { 8 | public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) 9 | { 10 | if (operation.operationId != "Products_GetAllByType") return; 11 | 12 | var response = operation.responses["200"]; 13 | response.vendorExtensions = new Dictionary(); 14 | response.vendorExtensions.Add("x-foo", "bar"); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/IndexerTypesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.Controllers 5 | { 6 | public class IndexerTypesController : ApiController 7 | { 8 | public int Create(Lookup lookup) 9 | { 10 | throw new NotImplementedException(); 11 | } 12 | } 13 | 14 | public class Lookup 15 | { 16 | public int TotalEntries { get; set; } 17 | 18 | public string this[string name] 19 | { 20 | get { throw new NotImplementedException(); } 21 | } 22 | 23 | public string this[int index] 24 | { 25 | get { throw new NotImplementedException(); } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/OverloadedAttributeRoutesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.Controllers 5 | { 6 | public class OverloadedAttributeRoutesController : ApiController 7 | { 8 | [Route("subscriptions/")] 9 | public void GetAll() 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | 14 | [Route("users/")] 15 | public void GetAll(int id) 16 | { 17 | throw new NotImplementedException(); 18 | } 19 | 20 | [Route("organisations/")] 21 | public void GetAll(int id, string name) 22 | { 23 | throw new NotImplementedException(); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/ApplySchemaVendorExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Swagger.Net.Dummy.SwaggerExtensions 4 | { 5 | public class ApplySchemaVendorExtensions : ISchemaFilter 6 | { 7 | public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type) 8 | { 9 | if (type.IsArray && type.GetElementType() != typeof(byte)) return; // Special case 10 | 11 | var nullableUnderlyingType = Nullable.GetUnderlyingType(type); 12 | 13 | schema.vendorExtensions.Add("x-type-dotnet", (nullableUnderlyingType ?? type).FullName); 14 | schema.vendorExtensions.Add("x-nullable", nullableUnderlyingType != null || !type.IsValueType); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Swagger.Net/SwaggerUi/IAssetProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Swagger.Net.SwaggerUi 5 | { 6 | public interface IAssetProvider 7 | { 8 | Asset GetAsset(string rootUrl, string assetPath); 9 | } 10 | 11 | public class Asset 12 | { 13 | public Asset(Stream stream, string mediaType) 14 | { 15 | Stream = stream; 16 | MediaType = mediaType; 17 | } 18 | 19 | public Stream Stream { get; private set; } 20 | 21 | public string MediaType { get; private set; } 22 | } 23 | 24 | public class AssetNotFound : Exception 25 | { 26 | public AssetNotFound(string message) 27 | : base(message) 28 | {} 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/NestingClassController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | 3 | namespace Swagger.Net.Dummy.Controllers 4 | { 5 | 6 | public class NestingBaseClassController : ApiController 7 | { 8 | public class NestedClass 9 | { 10 | /// 11 | /// Property that is of generic type taken from the class that this class is nested into 12 | /// 13 | public T GenericallyTypedProperty { get; set; } 14 | } 15 | } 16 | 17 | public class NestingClassController : NestingBaseClassController 18 | { 19 | public NestedClass Get(int x) 20 | { 21 | return new NestedClass(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Swagger.Net/Application/LicenseBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Swagger.Net.Application 2 | { 3 | public class LicenseBuilder 4 | { 5 | private string _name; 6 | private string _url; 7 | 8 | public LicenseBuilder Name(string name) 9 | { 10 | _name = name; 11 | return this; 12 | } 13 | 14 | public LicenseBuilder Url(string url) 15 | { 16 | _url = url; 17 | return this; 18 | } 19 | 20 | internal License Build() 21 | { 22 | if ((_name ?? _url) == null) return null; 23 | 24 | return new License 25 | { 26 | name = _name, 27 | url = _url 28 | }; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Swagger.Net/SwaggerUi/StreamExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace Swagger.Net.SwaggerUi 6 | { 7 | public static class StreamExtensions 8 | { 9 | public static Stream FindAndReplace(this Stream stream, IDictionary replacements) 10 | { 11 | var originalText = new StreamReader(stream).ReadToEnd(); 12 | var outputBuilder = new StringBuilder(originalText); 13 | 14 | foreach (var replacement in replacements) 15 | { 16 | outputBuilder.Replace(replacement.Key, replacement.Value); 17 | } 18 | 19 | return new MemoryStream(Encoding.UTF8.GetBytes(outputBuilder.ToString())); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/ArraysTestController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | using System.Collections.Generic; 3 | using Swagger.Net.Dummy.Types; 4 | 5 | namespace Swagger.Net.Dummy.Controllers 6 | { 7 | public class ArraysTestController : ApiController 8 | { 9 | [Route("list_string")] 10 | public List Get_list_string([FromUri] List p) 11 | { 12 | return p; 13 | } 14 | 15 | [Route("list_integer")] 16 | public List Get_list_int([FromUri] List p) 17 | { 18 | return p; 19 | } 20 | 21 | [Route("list_location")] 22 | public List Get_list_location([FromUri] List p) 23 | { 24 | return p; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Annotations/ApplySwaggerOperationFilterAttributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http.Description; 3 | 4 | namespace Swagger.Net.Annotations 5 | { 6 | public class ApplySwaggerOperationFilterAttributes : IOperationFilter 7 | { 8 | public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) 9 | { 10 | var attributes = apiDescription.GetControllerAndActionAttributes(); 11 | 12 | foreach (var attribute in attributes) 13 | { 14 | var filter = (IOperationFilter)Activator.CreateInstance(attribute.FilterType); 15 | filter.Apply(operation, schemaRegistry, apiDescription); 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Owin/MultiSwaggerOwinStartup.cs: -------------------------------------------------------------------------------- 1 | using Swagger.Net.Application; 2 | using System; 3 | using System.Web.Http; 4 | 5 | namespace Swagger.Net.Tests.Owin 6 | { 7 | public class MultiSwaggerOwinStartup : OwinStartup 8 | { 9 | public MultiSwaggerOwinStartup(params Type[] supportedControllers) : base(supportedControllers) 10 | { 11 | } 12 | 13 | protected override void EnableSwagger(HttpConfiguration config) 14 | { 15 | base.EnableSwagger(config); 16 | 17 | // configure swagger as well on separate URL 18 | config 19 | .EnableSwagger("docs/{apiVersion}/.metadata", c => c.SingleApiVersion("v1", "A title for your API")) 20 | .EnableSwaggerUi("docs-ui/{*assetPath}"); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Swagger/SwaggerTestBase.cs: -------------------------------------------------------------------------------- 1 | using Swagger.Net.Application; 2 | using System; 3 | 4 | namespace Swagger.Net.Tests.Swagger 5 | { 6 | public abstract class SwaggerTestBase : HttpMessageHandlerTestBase 7 | { 8 | protected SwaggerTestBase(string routeTemplate) : base(routeTemplate) {} 9 | 10 | protected void SetUpHandler(Action configure = null) 11 | { 12 | var swaggerDocsConfig = new SwaggerDocsConfig(); 13 | swaggerDocsConfig.SingleApiVersion("v1", "Test API"); 14 | swaggerDocsConfig.IncludeAllXmlComments(typeof(SwaggerTestBase).Assembly, AppDomain.CurrentDomain.BaseDirectory); 15 | 16 | configure?.Invoke(swaggerDocsConfig); 17 | 18 | Handler = new SwaggerDocsHandler(swaggerDocsConfig); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Swagger.Net/Application/HttpRouteDirectionConstraint.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net.Http; 3 | using System.Web.Http.Routing; 4 | 5 | namespace Swagger.Net.Application 6 | { 7 | public class HttpRouteDirectionConstraint : IHttpRouteConstraint 8 | { 9 | private readonly HttpRouteDirection _allowedDirection; 10 | 11 | public HttpRouteDirectionConstraint(HttpRouteDirection allowedDirection) 12 | { 13 | _allowedDirection = allowedDirection; 14 | } 15 | 16 | public bool Match( 17 | HttpRequestMessage request, 18 | IHttpRoute route, 19 | string parameterName, 20 | IDictionary values, 21 | HttpRouteDirection routeDirection) 22 | { 23 | return routeDirection == _allowedDirection; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/BlobController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.Controllers 5 | { 6 | public abstract class Blob : ApiController 7 | { 8 | /// GET 9 | public int Get(int? x) { return 0; } 10 | 11 | /// POST 12 | public int Post(int x) { return 0; } 13 | 14 | /// PUT 15 | public int Put(AnotherFoo x) { return 0; } 16 | 17 | /// PATCH 18 | public int Patch(AnotherFoo y, IEnumerable x) { return 0; } 19 | } 20 | 21 | public class Foo { } 22 | public class Bar { } 23 | public class Derp { } 24 | 25 | public class AnotherFoo { } 26 | 27 | public class BlobController : Blob { } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/ConflictingTypesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.Controllers 5 | { 6 | public class ConflictingTypesController : ApiController 7 | { 8 | public int Create(Requests.Blog blog) 9 | { 10 | throw new NotImplementedException(); 11 | } 12 | 13 | public Responses.Blog GetById(int id) 14 | { 15 | throw new NotImplementedException(); 16 | } 17 | } 18 | 19 | namespace Requests 20 | { 21 | public class Blog 22 | { 23 | public string Text { get; set; } 24 | } 25 | } 26 | namespace Responses 27 | { 28 | public class Blog 29 | { 30 | public int Id { get; set; } 31 | public string Text { get; set; } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/HandleFromUriParamsTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Swagger.Net.FromUriParams; 3 | using System; 4 | 5 | namespace Swagger.Net.Tests.CoreUnitTests 6 | { 7 | [TestFixture] 8 | class HandleFromUriParamsTests 9 | { 10 | [Test] 11 | public void ExtractAndAddQueryParams_null() 12 | { 13 | var target = new HandleFromUriParams(); 14 | Assert.Throws(() => target.ExtractAndAddQueryParams(null, "", false, null, null)); 15 | } 16 | 17 | [Test] 18 | public void ExtractAndAddQueryParams_empty() 19 | { 20 | var target = new HandleFromUriParams(); 21 | var schema = new Schema(); 22 | Assert.DoesNotThrow(() => target.ExtractAndAddQueryParams(schema, "", false, null, null)); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/PolymorphicTypesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Web.Http; 4 | 5 | namespace Swagger.Net.Dummy.Controllers 6 | { 7 | public class PolymorphicTypesController : ApiController 8 | { 9 | public int Create(Elephant elephant) 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | 14 | public IEnumerable GetAll() 15 | { 16 | throw new NotImplementedException(); 17 | } 18 | } 19 | 20 | public abstract class Animal 21 | { 22 | public string Type { get; set; } 23 | } 24 | 25 | public class Mamal : Animal 26 | { 27 | public string HairColor { get; set; } 28 | } 29 | 30 | public class Elephant : Mamal 31 | { 32 | public int TrunkLength { get; set; } 33 | } 34 | } -------------------------------------------------------------------------------- /Swagger.Net/Swagger/IModelFilter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Serialization; 2 | using System; 3 | 4 | namespace Swagger.Net 5 | { 6 | public interface IModelFilter 7 | { 8 | void Apply(Schema model, ModelFilterContext context); 9 | } 10 | 11 | public class ModelFilterContext 12 | { 13 | public ModelFilterContext( 14 | Type systemType, 15 | JsonObjectContract jsonObjectContract, 16 | SchemaRegistry schemaRegistry) 17 | { 18 | SystemType = systemType; 19 | JsonObjectContract = jsonObjectContract; 20 | SchemaRegistry = schemaRegistry; 21 | } 22 | 23 | public Type SystemType { get; private set; } 24 | 25 | public JsonObjectContract JsonObjectContract { get; private set; } 26 | 27 | public SchemaRegistry SchemaRegistry { get; private set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Swagger.Net/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle(SwaggerAssemb.Title)] 5 | [assembly: AssemblyDescription("")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCompany("")] 8 | [assembly: AssemblyProduct(SwaggerAssemb.Title)] 9 | [assembly: AssemblyCopyright("CopyLeft © 9999")] 10 | [assembly: AssemblyTrademark("")] 11 | [assembly: AssemblyCulture("")] 12 | [assembly: ComVisible(false)] 13 | [assembly: Guid("6a5ef762-7172-4bfb-98d2-076c3987fb7c")] 14 | 15 | [assembly: AssemblyVersion(SwaggerAssemb.Version)] 16 | [assembly: AssemblyFileVersion(SwaggerAssemb.Version)] 17 | [assembly: AssemblyInformationalVersion(SwaggerAssemb.Version)] 18 | 19 | public struct SwaggerAssemb 20 | { 21 | public const string Title = @"Swagger.Net.Core"; 22 | public const string Version = @"8.5.28.001"; 23 | } 24 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/JsonAnnotatedTypesController.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Converters; 3 | using System; 4 | using System.Web.Http; 5 | 6 | namespace Swagger.Net.Dummy.Controllers 7 | { 8 | public class JsonAnnotatedTypesController : ApiController 9 | { 10 | public int Create(JsonRequest request) 11 | { 12 | throw new NotImplementedException(); 13 | } 14 | } 15 | 16 | public class JsonRequest 17 | { 18 | [JsonIgnore] 19 | public string IgnoredProperty { get; set; } 20 | 21 | [JsonProperty("foobar")] 22 | public string CustomNamedProperty { get; set; } 23 | 24 | public Category Category { get; set; } 25 | } 26 | 27 | [JsonConverter(typeof(StringEnumConverter))] 28 | public enum Category 29 | { 30 | A = 2, 31 | B = 4 32 | } 33 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Swagger/SerializableTests.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NUnit.Framework; 3 | using Swagger.Net.Dummy.Controllers; 4 | 5 | 6 | namespace Swagger.Net.Tests.Swagger 7 | { 8 | [TestFixture] 9 | public class SerializableTests : SwaggerTestBase 10 | { 11 | public SerializableTests() : base("swagger/docs/{apiVersion}") { } 12 | 13 | [SetUp] 14 | public void SetUp() 15 | { 16 | SetUpDefaultRouteFor(); 17 | 18 | // Default set-up 19 | SetUpHandler(); 20 | } 21 | 22 | [Test] 23 | public void It_assigns_operation_properties_from_swagger_operation_attribute() 24 | { 25 | var swagger = GetContent(TEMP_URI.DOCS); 26 | var paths = swagger["paths"]; 27 | Assert.IsNotNull(paths); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/App_Start/CachingSwaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | 3 | namespace Swagger.Net.Dummy.App_Start 4 | { 5 | public class CachingSwaggerProvider : ISwaggerProvider 6 | { 7 | private static ConcurrentDictionary _cache = 8 | new ConcurrentDictionary(); 9 | 10 | private readonly ISwaggerProvider _swaggerProvider; 11 | 12 | public CachingSwaggerProvider(ISwaggerProvider swaggerProvider) 13 | { 14 | _swaggerProvider = swaggerProvider; 15 | } 16 | 17 | public SwaggerDocument GetSwagger(string rootUrl, string apiVersion) 18 | { 19 | var cacheKey = string.Format("{0}_{1}", rootUrl, apiVersion); 20 | return _cache.GetOrAdd(cacheKey, (key) => _swaggerProvider.GetSwagger(rootUrl, apiVersion)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Swagger.Net/Application/VersionInfoBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Swagger.Net.Application 5 | { 6 | public class VersionInfoBuilder 7 | { 8 | private readonly Dictionary _versionInfos; 9 | 10 | public VersionInfoBuilder() 11 | { 12 | _versionInfos = new Dictionary(); 13 | } 14 | 15 | public InfoBuilder Version(string version, string title) 16 | { 17 | version = version.ToUpper(); 18 | var infoBuilder = new InfoBuilder(version, title); 19 | _versionInfos[version] = infoBuilder; 20 | return infoBuilder; 21 | } 22 | 23 | public IDictionary Build() 24 | { 25 | return _versionInfos.ToDictionary(entry => entry.Key, entry => entry.Value.Build()); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/AddFileUploadParams.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http.Description; 2 | 3 | namespace Swagger.Net.Dummy.SwaggerExtensions 4 | { 5 | public class AddFileUploadParams : IOperationFilter 6 | { 7 | public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) 8 | { 9 | if (operation.operationId == "FileUpload_PostFormData") 10 | { 11 | operation.consumes.Add("application/form-data"); 12 | operation.parameters = new[] 13 | { 14 | new Parameter 15 | { 16 | name = "file", 17 | @in = "formData", 18 | required = true, 19 | type = "file" 20 | } 21 | }; 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/SupportFlaggedEnums.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Web.Http.Description; 3 | 4 | namespace Swagger.Net.Dummy.SwaggerExtensions 5 | { 6 | public class SupportFlaggedEnums : IOperationFilter 7 | { 8 | public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) 9 | { 10 | if (operation.parameters == null) return; 11 | 12 | var queryEnumParams = operation.parameters 13 | .Where(param => param.@in == "query" && param.@enum != null) 14 | .ToArray(); 15 | 16 | foreach (var param in queryEnumParams) 17 | { 18 | param.items = new PartialSchema { type = param.type, @enum = param.@enum }; 19 | param.type = "array"; 20 | param.collectionFormat = "csv"; 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Extensions/ApiParameterDescriptionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Linq; 4 | using System.Web.Http.Description; 5 | using Swagger.Net.Swagger.Annotations; 6 | 7 | namespace Swagger.Net.Swagger.Extensions 8 | { 9 | public static class ApiParameterDescriptionExtensions 10 | { 11 | public static RegularExpressionAttribute GetRegularExpressionAttribute(this ApiParameterDescription paramDesc) 12 | { 13 | return paramDesc.ParameterDescriptor.GetCustomAttributes().FirstOrDefault(); 14 | } 15 | 16 | public static SwaggerDescriptionAttribute GetDescriptionAttribute(this ApiParameterDescription paramDesc) 17 | { 18 | return paramDesc.ParameterDescriptor.GetCustomAttributes().FirstOrDefault(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Annotations/ApplySwaggerOperationAttributes.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Web.Http.Description; 3 | 4 | namespace Swagger.Net.Annotations 5 | { 6 | public class ApplySwaggerOperationAttributes : IOperationFilter 7 | { 8 | public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) 9 | { 10 | var attribute = apiDescription.ActionDescriptor.GetCustomAttributes() 11 | .FirstOrDefault(); 12 | if (attribute == null) return; 13 | 14 | if (attribute.OperationId != null) 15 | operation.operationId = attribute.OperationId; 16 | 17 | if (attribute.Tags != null) 18 | operation.tags = attribute.Tags; 19 | 20 | if (attribute.Schemes != null) 21 | operation.schemes = attribute.Schemes; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Swagger.Net/Application/ContactBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Swagger.Net.Application 2 | { 3 | public class ContactBuilder 4 | { 5 | private string _name; 6 | private string _url; 7 | private string _email; 8 | 9 | public ContactBuilder Name(string name) 10 | { 11 | _name = name; 12 | return this; 13 | } 14 | 15 | public ContactBuilder Url(string url) 16 | { 17 | _url = url; 18 | return this; 19 | } 20 | 21 | public ContactBuilder Email(string email) 22 | { 23 | _email = email; 24 | return this; 25 | } 26 | 27 | internal Contact Build() 28 | { 29 | if ((_name ?? _url ?? _email) == null) return null; 30 | 31 | return new Contact 32 | { 33 | name = _name, 34 | url = _url, 35 | email = _email 36 | }; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/ApplySwaggerResponseAttributesTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Swagger.Net.Annotations; 3 | 4 | namespace Swagger.Net.Tests.CoreUnitTests 5 | { 6 | [TestFixture] 7 | class ApplySwaggerResponseAttributesTests 8 | { 9 | [Test] 10 | public void InferDescriptionFrom_Empy() 11 | { 12 | string x = new ApplySwaggerResponseAttributes().InferDescriptionFrom(string.Empty); 13 | Assert.IsNull(x); 14 | } 15 | 16 | [Test] 17 | public void InferDescriptionFrom_Good() 18 | { 19 | string x = new ApplySwaggerResponseAttributes().InferDescriptionFrom("404"); 20 | Assert.AreEqual("NotFound", x); 21 | } 22 | 23 | [Test] 24 | public void InferDescriptionFrom_Bad() 25 | { 26 | string x = new ApplySwaggerResponseAttributes().InferDescriptionFrom("asdf"); 27 | Assert.IsNull(x); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.SelfHost/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/CustomersController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.Controllers 5 | { 6 | [RoutePrefix("Customers")] 7 | public class CustomersController : ApiController 8 | { 9 | public int Create(Customer customer, int adminLevel = 0) 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | 14 | [Route("{id?}")] 15 | public string Get(int id = 8) 16 | { 17 | return $"{id}"; 18 | } 19 | 20 | [HttpPut] 21 | [Route("{id}")] 22 | public void Update(int id, Customer customer) 23 | { 24 | throw new NotImplementedException(); 25 | } 26 | 27 | [Route("{id}")] 28 | public void Delete(int id) 29 | { 30 | throw new NotImplementedException(); 31 | } 32 | } 33 | 34 | public class Customer 35 | { 36 | public int Id { get; set; } 37 | public string Name { get; set; } 38 | } 39 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/PathRequiredController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Web.Http; 4 | 5 | namespace Swagger.Net.Dummy.Controllers 6 | { 7 | public class PathRequiredController : ApiController 8 | { 9 | public void Get(long id = 0) 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | 14 | public void Post(ComplexObject1 co) 15 | { 16 | throw new NotImplementedException(); 17 | } 18 | 19 | public void Put(ComplexObject2 co) 20 | { 21 | throw new NotImplementedException(); 22 | } 23 | } 24 | 25 | public class ComplexObject1 26 | { 27 | [Required] 28 | public int Id { get; set; } 29 | [Required] 30 | public string Name { get; set; } 31 | } 32 | 33 | public class ComplexObject2 34 | { 35 | [Required] 36 | public int Id; 37 | [Required] 38 | public string Name; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Annotations/ApplySwaggerSchemaFilterAttributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Swagger.Net.Annotations 6 | { 7 | public class ApplySwaggerSchemaFilterAttributes : ISchemaFilter 8 | { 9 | private IEnumerable Attributes(Type type) 10 | { 11 | var attributes = type.GetCustomAttributes(true).OfType(); 12 | if (attributes.Count() == 0 && type.BaseType != null) 13 | return Attributes(type.BaseType); 14 | else 15 | return attributes; 16 | } 17 | 18 | public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type) 19 | { 20 | foreach (var attribute in Attributes(type)) 21 | { 22 | var filter = (ISchemaFilter)Activator.CreateInstance(attribute.FilterType); 23 | filter.Apply(schema, schemaRegistry, type); 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/DataContractAnnotatedTypesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | using System.Web.Http; 4 | 5 | namespace Swagger.Net.Dummy.Controllers 6 | { 7 | public class DataContractAnnotatedTypesController : ApiController 8 | { 9 | public int Create(DataContractRequest request) 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | } 14 | 15 | public class DataContractRequest 16 | { 17 | public DataContractEnumWithOutValues OverriddenValues { get; set; } 18 | public DataContractEnumWithValues RawValues { get; set; } 19 | } 20 | 21 | [DataContract] 22 | public enum DataContractEnumWithValues 23 | { 24 | [EnumMember(Value = "aie")] 25 | A = 2, 26 | [EnumMember(Value = "bee")] 27 | B = 4 28 | } 29 | 30 | [DataContract] 31 | public enum DataContractEnumWithOutValues 32 | { 33 | [EnumMember] 34 | A = 2, 35 | [EnumMember] 36 | B = 4 37 | } 38 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/ProtectedResourcesController.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System; 3 | using System.Web.Http; 4 | 5 | namespace Swagger.Net.Dummy.Controllers 6 | { 7 | public class ProtectedResourcesController : ApiController 8 | { 9 | [Authorize(Roles = "read")] 10 | public JObject Get(int id) 11 | { 12 | throw new NotImplementedException(); 13 | } 14 | } 15 | 16 | public class ProtectedWithCustomAttributeResourcesController : ApiController 17 | { 18 | [AuthorizedRoles(Role.Read,Role.Write)] 19 | public JObject Get(int id) 20 | { 21 | throw new NotImplementedException(); 22 | } 23 | } 24 | 25 | public class AuthorizedRolesAttribute : AuthorizeAttribute 26 | { 27 | public AuthorizedRolesAttribute(params Role[] roles) : base() 28 | { 29 | Roles = String.Join(",", Enum.GetNames(typeof(Role))); 30 | } 31 | } 32 | 33 | public enum Role 34 | { 35 | Read, Write, Delete 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/ApplyXmlTypeCommentsTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Swagger.Net.XmlComments; 3 | using System; 4 | using System.IO; 5 | using System.Linq; 6 | 7 | namespace Swagger.Net.Tests.CoreUnitTests 8 | { 9 | [TestFixture] 10 | class ApplyXmlTypeCommentsTests 11 | { 12 | [Test] 13 | public void ApplyXmlTypeComments_Empty() 14 | { 15 | Assert.Throws(() => new ApplyXmlTypeComments("")); 16 | } 17 | 18 | [Test] 19 | public void ApplyXmlTypeComments_Test() 20 | { 21 | string directory = AppDomain.CurrentDomain.BaseDirectory; 22 | string xmlFile = Directory.GetFiles(directory, "*.XML", SearchOption.AllDirectories).FirstOrDefault(); 23 | if (string.IsNullOrEmpty(xmlFile)) 24 | { 25 | Assert.Inconclusive(); 26 | } 27 | else 28 | { 29 | Assert.DoesNotThrow(() => new ApplyXmlTypeComments(xmlFile)); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Annotations/SwaggerExampleAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Swagger.Net.Annotations 4 | { 5 | /// 6 | /// Allows you to add an example value for a parameter 7 | /// 8 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 9 | public class SwaggerExampleAttribute : Attribute 10 | { 11 | /// 12 | /// Constructor 13 | /// 14 | /// Parameter name 15 | /// Example value for a parameter 16 | public SwaggerExampleAttribute(string parameterName, object example) 17 | { 18 | this.ParameterName = parameterName; 19 | this.Example = example; 20 | } 21 | 22 | /// 23 | /// Parameter name 24 | /// 25 | public string ParameterName { get; private set; } 26 | 27 | /// 28 | /// Example value for a parameter 29 | /// 30 | public object Example { get; set; } 31 | } 32 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Converters; 2 | using Newtonsoft.Json.Serialization; 3 | using System.Web.Http; 4 | using System.Web.Http.Cors; 5 | 6 | namespace Swagger.Net.Dummy 7 | { 8 | public static class WebApiConfig 9 | { 10 | public static void Register(HttpConfiguration config) 11 | { 12 | // Web API configuration and services 13 | config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 14 | 15 | // Web API routes 16 | config.MapHttpAttributeRoutes(); 17 | 18 | config.Routes.MapHttpRoute( 19 | name: "DefaultApi", 20 | routeTemplate: "{controller}/{id}", 21 | defaults: new { id = RouteParameter.Optional } 22 | ); 23 | 24 | var formatter = config.Formatters.JsonFormatter; 25 | formatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 26 | 27 | formatter.SerializerSettings.Converters.Add(new StringEnumConverter()); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/SelfReferencingTypesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Web.Http; 4 | 5 | namespace Swagger.Net.Dummy.Controllers 6 | { 7 | public class SelfReferencingTypesController : ApiController 8 | { 9 | public int Create(Component component) 10 | { 11 | throw new NotImplementedException(); 12 | } 13 | 14 | // Breaks current swagger-ui 15 | //public ListOfSelf Get() 16 | //{ 17 | // throw new NotImplementedException(); 18 | //} 19 | 20 | [HttpPut] 21 | public void Update(DictionaryOfSelf values) 22 | { 23 | throw new NotImplementedException(); 24 | } 25 | } 26 | 27 | public class Component 28 | { 29 | public string Name { get; set; } 30 | public IEnumerable SubComponents { get; set; } 31 | } 32 | 33 | public class ListOfSelf : List 34 | {} 35 | 36 | public class DictionaryOfSelf : Dictionary 37 | {} 38 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/BaseController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http; 3 | 4 | namespace Swagger.Net.Dummy.Controllers 5 | { 6 | public abstract class BaseController : ApiController 7 | { 8 | 9 | /// 10 | /// Get a record by the given key. 11 | /// 12 | public virtual T Get(T id) 13 | { 14 | return id; 15 | } 16 | 17 | /// 18 | /// Post a record. 19 | /// 20 | public virtual long Post([FromBody]string value) 21 | { 22 | return DateTime.Now.Ticks; 23 | } 24 | 25 | /// 26 | /// Put a record by the given key. 27 | /// 28 | public virtual T Put(T id, [FromBody]string value) 29 | { 30 | return id; 31 | } 32 | 33 | /// 34 | /// Delete a record by the given key. 35 | /// 36 | public virtual long Delete(long id) 37 | { 38 | return id; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Annotations/ApplySwaggerExampleAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Web.Http.Description; 3 | 4 | namespace Swagger.Net.Annotations 5 | { 6 | /// 7 | /// Apply a ApplySwaggerExampleAttribute 8 | /// 9 | public class ApplySwaggerExampleAttribute : IOperationFilter 10 | { 11 | public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) 12 | { 13 | if (operation.parameters == null) 14 | { 15 | return; 16 | } 17 | 18 | var customAttributes = apiDescription.ActionDescriptor.GetCustomAttributes(); 19 | 20 | foreach (Parameter parameter in operation.parameters) 21 | { 22 | SwaggerExampleAttribute customAttribute = customAttributes.FirstOrDefault(p => p.ParameterName == parameter.name); 23 | if (customAttribute != null) 24 | { 25 | parameter.example = customAttribute.Example; 26 | } 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/SwaggerDocsConfigTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Swagger.Net.Application; 3 | using Swagger.Net.Dummy; 4 | 5 | namespace Swagger.Net.Tests.CoreUnitTests 6 | { 7 | [TestFixture] 8 | class SwaggerDocsConfigTests 9 | { 10 | [Test] 11 | public void RootUrl_Test() 12 | { 13 | var sc = new SwaggerDocsConfig(); 14 | Assert.DoesNotThrow(() => sc.RootUrl(null)); 15 | } 16 | 17 | [Test] 18 | public void CustomProvider_Test() 19 | { 20 | var sc = new SwaggerDocsConfig(); 21 | Assert.DoesNotThrow(() => sc.CustomProvider(null)); 22 | } 23 | 24 | [Test] 25 | public void ModelFilter1_Test() 26 | { 27 | var sc = new SwaggerDocsConfig(); 28 | Assert.DoesNotThrow(() => sc.ModelFilter(null)); 29 | } 30 | 31 | [Test] 32 | public void ModelFilter2_Test() 33 | { 34 | var sc = new SwaggerDocsConfig(); 35 | Assert.DoesNotThrow(() => sc.ModelFilter()); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Swagger/SchemaTestsWithCustomAuthorizeAttribute.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Swagger.Net.Dummy.Controllers; 3 | using System.Linq; 4 | 5 | namespace Swagger.Net.Tests.Swagger 6 | { 7 | [TestFixture] 8 | public class SchemaTestsWithCustomAuthorizeAttribute : SwaggerTestBase 9 | { 10 | public SchemaTestsWithCustomAuthorizeAttribute() : base("swagger/docs/{apiVersion}") {} 11 | 12 | [Test] 13 | public void It_adds_security_to_protected_operations() 14 | { 15 | SetUpDefaultRouteFor(); 16 | SetUpDefaultRouteFor(); 17 | 18 | SetUpHandler(c => 19 | { 20 | c.ApiKey("apiKey", "header", "API Key Authentication"); 21 | }); 22 | 23 | var swagger = GetContent(TEMP_URI.DOCS); 24 | 25 | var paths = swagger.paths.ToArray(); 26 | 27 | Assert.IsNotNull(paths[0].Value.get.security); 28 | Assert.IsNotNull(paths[1].Value.get.security); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/JsonPropertyExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Serialization; 2 | using NUnit.Framework; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace Swagger.Net.Tests.CoreUnitTests 6 | { 7 | [TestFixture] 8 | class JsonPropertyExtensionsTests 9 | { 10 | [Test] 11 | public void FieldInfo_Null() 12 | { 13 | var x = new JsonProperty(); 14 | x.UnderlyingName = null; 15 | Assert.IsNull(x.FieldInfo()); 16 | } 17 | 18 | [Test] 19 | public void FieldInfo_Test() 20 | { 21 | var x = new JsonProperty(); 22 | x.UnderlyingName = "test"; 23 | x.DeclaringType = typeof(Test); 24 | Assert.IsNull(x.FieldInfo()); 25 | } 26 | } 27 | 28 | [MetadataType(typeof(int))] 29 | public class Test 30 | { 31 | [MetadataType(typeof(int))] 32 | public class Meta { } 33 | 34 | public int id { get; set; } 35 | public MetadataTypeAttribute attrib1 { get; set; } 36 | public Meta attrib2 { get; set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Swagger/XmlComments2Tests.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NUnit.Framework; 3 | using Swagger.Net.Dummy.Controllers; 4 | 5 | namespace Swagger.Net.Tests.Swagger 6 | { 7 | [TestFixture] 8 | public class XmlComments2Tests : SwaggerTestBase 9 | { 10 | public XmlComments2Tests() : base("swagger/docs/{apiVersion}") { } 11 | 12 | [SetUp] 13 | public void SetUp() 14 | { 15 | SetUpAttributeRoutesFrom(typeof(BlobController).Assembly); 16 | SetUpDefaultRouteFor(); 17 | SetUpHandler(); 18 | } 19 | 20 | [Test] 21 | public void It_documents_operations_from_inherited_actions() 22 | { 23 | var swagger = GetContent(TEMP_URI.DOCS); 24 | Assert.IsNotNull(swagger); 25 | 26 | var path = swagger["paths"]["/blob"]; 27 | Assert.IsNotNull(path); 28 | 29 | Assert.IsNotNull(path["post"]["summary"]); 30 | Assert.IsNotNull(path["get"]["summary"]); 31 | Assert.IsNotNull(path["patch"]["summary"]); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Extensions/JsonContractExtensions.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Serialization; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Swagger.Net 6 | { 7 | public static class JsonContractExtensions 8 | { 9 | private static IEnumerable AmbiguousTypeNames = new[] 10 | { 11 | "System.Object", 12 | "System.Net.Http.HttpRequestMessage", 13 | "System.Net.Http.HttpResponseMessage", 14 | "System.Web.Http.IHttpActionResult" 15 | }; 16 | 17 | public static bool IsSelfReferencing(this JsonDictionaryContract dictionaryContract) 18 | { 19 | return dictionaryContract.UnderlyingType == dictionaryContract.DictionaryValueType; 20 | } 21 | 22 | public static bool IsSelfReferencing(this JsonArrayContract arrayContract) 23 | { 24 | return arrayContract.UnderlyingType == arrayContract.CollectionItemType; 25 | } 26 | 27 | public static bool IsAmbiguous(this JsonObjectContract objectContract) 28 | { 29 | return AmbiguousTypeNames.Contains(objectContract.UnderlyingType.FullName); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/TestBindingController.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using System.Web.Http; 4 | using System.Web.Http.Controllers; 5 | using System.Web.Http.Metadata; 6 | 7 | namespace Swagger.Net.Dummy.Controllers 8 | { 9 | 10 | public class TestBindingController : ApiController 11 | { 12 | [Route("{id}")] 13 | public string Get([FromUri]string id) 14 | { 15 | return "abc"; 16 | } 17 | 18 | [Route("NoBind")] 19 | public string Get2() 20 | { 21 | return "abc"; 22 | } 23 | } 24 | 25 | public class BindAttribute : ParameterBindingAttribute 26 | { 27 | public override HttpParameterBinding GetBinding(HttpParameterDescriptor p) 28 | { 29 | return new FooBinding(p); 30 | } 31 | } 32 | public class FooBinding : HttpParameterBinding 33 | { 34 | public FooBinding(HttpParameterDescriptor d) : base(d) { } 35 | 36 | public override Task ExecuteBindingAsync(ModelMetadataProvider m, HttpActionContext a, CancellationToken c) 37 | { 38 | return Task.FromResult(true); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/ApplySwaggerOperationAttributesTests.cs: -------------------------------------------------------------------------------- 1 | using Moq; 2 | using NUnit.Framework; 3 | using Swagger.Net.Annotations; 4 | using System.Collections.ObjectModel; 5 | using System.Web.Http.Controllers; 6 | using System.Web.Http.Description; 7 | 8 | namespace Swagger.Net.Tests.CoreUnitTests 9 | { 10 | [TestFixture] 11 | class ApplySwaggerOperationAttributesTests 12 | { 13 | [Test] 14 | public void Apply_null() 15 | { 16 | var collSwagger = new Collection() 17 | { 18 | new SwaggerOperationAttribute() 19 | { 20 | OperationId = null, 21 | Tags = null, 22 | Schemes = null 23 | } 24 | }; 25 | 26 | var mock = new Mock(); 27 | mock.Setup(x => x.GetCustomAttributes()).Returns(collSwagger); 28 | 29 | var apiDescription = new ApiDescription() { ActionDescriptor = mock.Object }; 30 | var op = new ApplySwaggerOperationAttributes(); 31 | Assert.DoesNotThrow(() => op.Apply(null, null, apiDescription)); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/ObsoleteActionsController.cs: -------------------------------------------------------------------------------- 1 | using Swagger.Net.Swagger.Annotations; 2 | using System; 3 | using System.Web.Http; 4 | using System.Web.Http.Description; 5 | 6 | namespace Swagger.Net.Dummy.Controllers 7 | { 8 | public class ObsoleteActionsController : ApiController 9 | { 10 | [HttpPut] 11 | public int Update(int id, string value) 12 | { 13 | throw new NotImplementedException(); 14 | } 15 | 16 | [Obsolete("OBSOLETE_DELETE")] 17 | public void Delete(int id) 18 | { 19 | throw new NotImplementedException(); 20 | } 21 | 22 | [SwaggerDescription(summary: "abc")] 23 | [Obsolete("OBSOLETE_POST")] 24 | public void Post(int id) 25 | { 26 | throw new NotImplementedException(); 27 | } 28 | 29 | [SwaggerDescription("abc", "def")] 30 | [Obsolete("OBSOLETE_POST")] 31 | public void Patch(int id) 32 | { 33 | throw new NotImplementedException(); 34 | } 35 | 36 | [Obsolete()] 37 | [ResponseType(typeof(int))] 38 | public int Get(int id) 39 | { 40 | throw new NotImplementedException(); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Swagger.Net/Swagger/XmlComments/XPathNavigatorExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using System.Xml.XPath; 3 | 4 | namespace Swagger.Net.XmlComments 5 | { 6 | public static class XPathNavigatorExtensions 7 | { 8 | public static Regex ParamPattern = new Regex(@"<(see|paramref) (name|cref)=""([TPF]{1}:)?(?.+?)"" />"); 9 | public static Regex ConstPattern = new Regex(@"(?.+?)"); 10 | 11 | public static string ExtractContent(this XPathNavigator node) 12 | { 13 | if (node == null) return null; 14 | 15 | return XmlTextHelper.NormalizeIndentation( 16 | ConstPattern.Replace( 17 | ParamPattern.Replace(node.InnerXml, GetParamRefName), 18 | GetConstRefName) 19 | ); 20 | } 21 | 22 | public static string GetConstRefName(Match match) 23 | { 24 | if (match.Groups.Count != 2) return null; 25 | 26 | return match.Groups["display"].Value; 27 | } 28 | 29 | public static string GetParamRefName(Match match) 30 | { 31 | if (match.Groups.Count != 5) return null; 32 | 33 | return "{" + match.Groups["display"].Value + "}"; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/AssignOAuth2SecurityRequirements.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Web.Http; 4 | using System.Web.Http.Description; 5 | 6 | namespace Swagger.Net.Dummy.SwaggerExtensions 7 | { 8 | public class AssignOAuth2SecurityRequirements : IOperationFilter 9 | { 10 | public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) 11 | { 12 | // Correspond each "Authorize" role to an oauth2 scope 13 | var scopes = apiDescription.ActionDescriptor.GetFilterPipeline() 14 | .Select(filterInfo => filterInfo.Instance) 15 | .OfType() 16 | .SelectMany(attr => attr.Roles.Split(',')) 17 | .Distinct(); 18 | 19 | if (scopes.Any()) 20 | { 21 | if (operation.security == null) 22 | operation.security = new List>>(); 23 | 24 | var oAuthRequirements = new Dictionary> 25 | { 26 | { "oauth2", scopes } 27 | }; 28 | 29 | operation.security.Add(oAuthRequirements); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Swagger.Net/Application/RedirectHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Http; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Swagger.Net.Application 8 | { 9 | public class RedirectHandler : HttpMessageHandler 10 | { 11 | private readonly Func _rootUrlResolver; 12 | private readonly string _redirectPath; 13 | 14 | public RedirectHandler(Func rootUrlResolver, string redirectPath) 15 | { 16 | _rootUrlResolver = rootUrlResolver; 17 | _redirectPath = redirectPath; 18 | } 19 | 20 | protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 21 | { 22 | var redirectUrl = _rootUrlResolver(request) + "/" + _redirectPath; 23 | 24 | var response = request.CreateResponse(HttpStatusCode.Moved); 25 | response.Headers.Location = new Uri(redirectUrl); 26 | 27 | var tsc = new TaskCompletionSource(); 28 | tsc.SetResult(response); 29 | return tsc.Task; 30 | } 31 | 32 | public Task SendAsync(HttpRequestMessage request) 33 | { 34 | return SendAsync(request, new CancellationToken()); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/SwaggerEnabledConfigurationTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Swagger.Net.Application; 3 | using System.Web.Http; 4 | 5 | namespace Swagger.Net.Tests.CoreUnitTests 6 | { 7 | [TestFixture] 8 | class SwaggerEnabledConfigurationTests 9 | { 10 | private SwaggerEnabledConfiguration swag 11 | { 12 | get 13 | { 14 | return new SwaggerEnabledConfiguration( 15 | new HttpConfiguration(), new SwaggerDocsConfig(), "" 16 | ); 17 | } 18 | } 19 | 20 | [Test] 21 | public void EnableSwaggerUi_Test() 22 | { 23 | Assert.DoesNotThrow(() => swag.EnableSwaggerUi("test", c => { })); 24 | } 25 | 26 | [Test] 27 | public void EnableSwaggerUi_Null() 28 | { 29 | Assert.DoesNotThrow(() => swag.EnableSwaggerUi("test", null)); 30 | } 31 | 32 | [Test] 33 | public void EnableSwaggerUi_NullApiKeyScheme() 34 | { 35 | var httpConfig = new HttpConfiguration(); 36 | var config = new SwaggerDocsConfig(); 37 | config.ApiKey("test", "1", ""); 38 | var s = new SwaggerEnabledConfiguration(httpConfig, config, ""); 39 | Assert.DoesNotThrow(() => s.EnableSwaggerUi("test", c => { })); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/ProductsController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.Serialization; 4 | using System.Web.Http; 5 | 6 | namespace Swagger.Net.Dummy.Controllers 7 | { 8 | public class ProductsController : ApiController 9 | { 10 | public int Create(Product product) 11 | { 12 | throw new NotImplementedException(); 13 | } 14 | 15 | public Product GetById(int id) 16 | { 17 | throw new NotImplementedException(); 18 | } 19 | 20 | [HttpGet] 21 | public IEnumerable GetAllByType(ProductType type) 22 | { 23 | throw new NotImplementedException(); 24 | } 25 | 26 | [HttpOptions] 27 | public int Options() 28 | { 29 | return 0; 30 | } 31 | 32 | [HttpPatch] 33 | public int Patch() 34 | { 35 | return 0; 36 | } 37 | } 38 | 39 | public class Product 40 | { 41 | public int Id { get; internal set; } 42 | public ProductType Type { get; set; } 43 | public string Description { get; set; } 44 | public decimal UnitPrice { get; set; } 45 | } 46 | 47 | public enum ProductType 48 | { 49 | [EnumMember(Value = "Publication")] 50 | Book = 2, 51 | 52 | Album = 4 53 | } 54 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Swagger/FromHeaderParamsTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Swagger.Net.Dummy.Controllers; 3 | using System.Linq; 4 | 5 | namespace Swagger.Net.Tests.Swagger 6 | { 7 | [TestFixture] 8 | public class FromHeaderParamsTests : SwaggerTestBase 9 | { 10 | public FromHeaderParamsTests() : base("swagger/docs/{apiVersion}") { } 11 | 12 | [SetUp] 13 | public void SetUp() 14 | { 15 | SetUpDefaultRouteFor(); 16 | SetUpHandler(); 17 | } 18 | 19 | [Test] 20 | public void It_sets_headers() 21 | { 22 | var swagger = GetContent(TEMP_URI.DOCS); 23 | foreach (var path in swagger.paths) 24 | { 25 | validate(path.Value.get); 26 | validate(path.Value.post); 27 | } 28 | 29 | void validate(Operation op) 30 | { 31 | foreach(var param in op.parameters) 32 | Assert.AreEqual("header", param.@in); 33 | } 34 | } 35 | 36 | [Test] 37 | public void It_sets_headers_with_different_name() 38 | { 39 | var swagger = GetContent(TEMP_URI.DOCS); 40 | Assert.AreEqual("abctest", swagger.paths.First().Value.post.parameters.First().name); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.WebHost/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /Swagger.Net/Nuget/Tools/Init.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | If ($project -eq $null) { return } 3 | $SwaggerConfig = $project.ProjectItems.Item("SwaggerConfig.cs").FileNames(1) 4 | 5 | function MoveConfigFile($AppStartDir) 6 | { 7 | Write-Host $AppStartDir 8 | $ValidPath = Test-Path $AppStartDir 9 | If ($ValidPath -eq $True) 10 | { 11 | $AppSwaggerConfig = Join-Path $AppStartDir "SwaggerConfig.cs" 12 | Write-Host $AppSwaggerConfig 13 | $ValidFile = Test-Path $AppSwaggerConfig 14 | If ($ValidFile -eq $False) 15 | { 16 | $item = $project.ProjectItems.Item("SwaggerConfig.cs") 17 | $item.Open('{7651A701-06E5-11D1-8EBD-00A0C90F26EA}') 18 | $item.SaveAs($AppSwaggerConfig) 19 | Write-Host "SwaggerConfig Saved! " 20 | } 21 | } 22 | } 23 | 24 | try 25 | { 26 | $AppStartDir = $project.ProjectItems.Item("App_Start").FileNames(1) 27 | MoveConfigFile $AppStartDir 28 | } 29 | catch 30 | { 31 | Write-Host "App_Start not found! " 32 | #Write-Host $_.Exception 33 | } 34 | 35 | 36 | try 37 | { 38 | $project.ProjectItems.Item("SwaggerConfig.cs").Remove() 39 | } 40 | catch 41 | { 42 | Write-Host "Error removing SwaggerConfig file! " 43 | #Write-Host $_.Exception 44 | } 45 | 46 | try 47 | { 48 | Remove-Item $SwaggerConfig 49 | } 50 | catch 51 | { 52 | Write-Host "Error deleting SwaggerConfig file! " 53 | #Write-Host $_.Exception 54 | } 55 | 56 | 57 | -------------------------------------------------------------------------------- /Swagger.Net/Swagger.Net.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Swagger-Net 5 | $version$ 6 | Swagger-Net | Swagger for WebApi 7 | Swagger-Net Team 8 | LICENSE.TXT 9 | https://github.com/heldersepu/Swagger-Net 10 | false 11 | Add a Swagger(ui 3.x) to your WebApi projects! 12 | Combines ApiExplorer and Swagger/swagger-ui(3.x) to provide documentation and playground to your API. 13 | https://avatars2.githubusercontent.com/u/7658037 14 | CopyLeft 2020 15 | Swagger SwaggerUi Documentation Discovery Help WebApi AspNet AspNetWebApi Docs SelfHost OWIN 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/FileDownloadController.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Net.Http; 3 | using System.Net.Http.Headers; 4 | using System.Web.Http; 5 | 6 | namespace Swagger.Net.Dummy.Controllers 7 | { 8 | [RoutePrefix("FileDownload")] 9 | public class FileDownloadController : ApiController 10 | { 11 | [HttpGet] 12 | public HttpResponseMessage GetFile() 13 | { 14 | var response = new HttpResponseMessage(); 15 | var stream = new FileStream(@"c:\test.txt", FileMode.Open); 16 | response.Content = new StreamContent(stream); 17 | response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); 18 | return response; 19 | } 20 | 21 | [HttpGet] 22 | [Route("filename")] 23 | public HttpResponseMessage GetFileWithFilename() 24 | { 25 | var response = new HttpResponseMessage(); 26 | var stream = new FileStream(@"c:\test.txt", FileMode.Open); 27 | response.Content = new StreamContent(stream); 28 | response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); 29 | response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") 30 | { 31 | FileName = "test.txt" 32 | }; 33 | return response; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/DynamicTypesController.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.Dynamic; 7 | using System.Net.Http; 8 | using System.Web.Http; 9 | 10 | namespace Swagger.Net.Dummy.Controllers 11 | { 12 | public class DynamicTypesController : ApiController 13 | { 14 | public int Create(dynamic thing) 15 | { 16 | throw new NotImplementedException(); 17 | } 18 | 19 | [HttpPut] 20 | public void Update(DynamicObjectSubType resource) 21 | { 22 | } 23 | 24 | public ExpandoObject GeByProperties(JObject thing) 25 | { 26 | throw new NotImplementedException(); 27 | } 28 | 29 | public IEnumerable GetAll() 30 | { 31 | throw new NotImplementedException(); 32 | } 33 | 34 | public HttpResponseMessage Delete(int id) 35 | { 36 | throw new NotImplementedException(); 37 | } 38 | 39 | public IHttpActionResult Head() 40 | { 41 | throw new NotImplementedException(); 42 | } 43 | } 44 | 45 | [JsonObject] 46 | public class DynamicObjectSubType : DynamicObject 47 | { 48 | [Range(1.1, 9.9)] 49 | public double Id; 50 | 51 | public string Name { get; set; } 52 | } 53 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.WebHost/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.WebHost/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Swagger.Net.Dummy.WebHost")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("Swagger.Net.Dummy.WebHost")] 12 | [assembly: AssemblyCopyright("CopyLeft © 2020")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("a1251d8f-a8d7-40aa-bc76-c6767acdffba")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Revision and Build Numbers 32 | // by using the '*' as shown below: 33 | [assembly: AssemblyVersion("1.0.0.0")] 34 | [assembly: AssemblyFileVersion("1.0.0.0")] 35 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Swagger/SchemaArraysTests.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NUnit.Framework; 3 | using Swagger.Net.Dummy.Controllers; 4 | 5 | namespace Swagger.Net.Tests.Swagger 6 | { 7 | [TestFixture] 8 | public class SchemaArraysTests : SwaggerTestBase 9 | { 10 | public SchemaArraysTests() : base("swagger/docs/{apiVersion}") { } 11 | 12 | [SetUp] 13 | public void SetUp() 14 | { 15 | // Default set-up 16 | SetUpHandler(); 17 | } 18 | 19 | [TestCase("string", "type")] 20 | [TestCase("integer", "type")] 21 | [TestCase("location", "$ref")] 22 | public void It_handles_arrays(string type, string item) 23 | { 24 | SetUpAttributeRoutesFrom(typeof(ArraysTestController).Assembly); 25 | 26 | var swagger = GetContent(TEMP_URI.DOCS); 27 | var get = swagger["paths"]["/list_" + type]["get"]; 28 | var param = get["parameters"][0]; 29 | var resp_schema = get["responses"]["200"]["schema"]; 30 | 31 | Assert.AreEqual("p", param["name"].ToString()); 32 | Assert.AreEqual("array", param["type"].ToString()); 33 | 34 | Assert.AreEqual("array", resp_schema["type"].ToString()); 35 | Assert.AreEqual("array", resp_schema["type"].ToString()); 36 | var resp_item = resp_schema["items"][item].ToString(); 37 | Assert.IsTrue(resp_item.ToLower().EndsWith(type), resp_item); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Swagger.Net.Tests")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("Swagger.Net.Tests")] 12 | [assembly: AssemblyCopyright("CopyLeft © 2020")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("71027952-e5e8-474f-aa84-aac76244ddc8")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Swaskhbuckle.Dummy.Core")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("Swaskhbuckle.Dummy.Core")] 12 | [assembly: AssemblyCopyright("CopyLeft © 2020")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("115bc2b9-0830-42ef-9fd8-46d0952a5f55")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.SelfHost/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Swagger.Net.Dummy.SelfHost")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("Swagger.Net.Dummy.SelfHost")] 12 | [assembly: AssemblyCopyright("CopyLeft © 2020")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("ca59cc1f-8a9c-477f-820c-3a7db5ac43b5")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/SwaggerExtensions/AppendVersionToBasePath.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Web.Http.Description; 3 | 4 | namespace Swagger.Net.Dummy.SwaggerExtensions 5 | { 6 | public class AppendVersionToBasePath : IDocumentFilter 7 | { 8 | public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer) 9 | { 10 | swaggerDoc.basePath = "/" + swaggerDoc.info.version; 11 | 12 | swaggerDoc.paths = swaggerDoc.paths.ToDictionary( 13 | entry => entry.Key.Replace("/{apiVersion}", ""), 14 | entry => RemoveVersionParamsFrom(entry.Value)); 15 | } 16 | 17 | private PathItem RemoveVersionParamsFrom(PathItem pathItem) 18 | { 19 | RemoveVersionParamFrom(pathItem.get); 20 | RemoveVersionParamFrom(pathItem.put); 21 | RemoveVersionParamFrom(pathItem.post); 22 | RemoveVersionParamFrom(pathItem.delete); 23 | RemoveVersionParamFrom(pathItem.options); 24 | RemoveVersionParamFrom(pathItem.head); 25 | RemoveVersionParamFrom(pathItem.patch); 26 | return pathItem; 27 | } 28 | 29 | private void RemoveVersionParamFrom(Operation operation) 30 | { 31 | if (operation == null) return; 32 | 33 | var versionParam = operation.parameters.SingleOrDefault(param => param.name == "apiVersion"); 34 | if (versionParam != null) operation.parameters.Remove(versionParam); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/SchemaExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using Moq; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Serialization; 4 | using NUnit.Framework; 5 | using System; 6 | using System.Collections.Generic; 7 | 8 | namespace Swagger.Net.Tests.CoreUnitTests 9 | { 10 | [TestFixture] 11 | class SchemaExtensionsTests 12 | { 13 | Schema schema 14 | { 15 | get 16 | { 17 | var schemaRegistry = new SchemaRegistry( 18 | new JsonSerializerSettings(), 19 | new SwaggerGeneratorOptions()); 20 | return schemaRegistry.CreateInlineSchema(typeof(object)); 21 | } 22 | } 23 | 24 | [Test] 25 | public void WithValidationProperties_Null() 26 | { 27 | var mock = new Mock(); 28 | Assert.IsNotNull(schema.WithValidationProperties(mock.Object)); 29 | } 30 | 31 | [Test] 32 | public void PopulateFrom_Null() 33 | { 34 | var partSchema = new PartialSchema(); 35 | Schema nullSchema = null; 36 | Assert.DoesNotThrow(() => partSchema.PopulateFrom(nullSchema)); 37 | } 38 | 39 | [Test] 40 | public void PopulateFrom_Simple() 41 | { 42 | var partSchema = new PartialSchema(); 43 | var schema = new Schema { pattern = "abc" }; 44 | partSchema.PopulateFrom(schema); 45 | Assert.AreEqual(schema.pattern, partSchema.pattern); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020, Swagger-Net Team 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/AnnotatedTypesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Net; 4 | using System.Net.Http; 5 | using System.Web.Http; 6 | 7 | namespace Swagger.Net.Dummy.Controllers 8 | { 9 | public class AnnotatedTypesController : ApiController 10 | { 11 | public int Create(Payment payment) 12 | { 13 | if (!ModelState.IsValid) 14 | throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); 15 | 16 | throw new NotImplementedException(); 17 | } 18 | } 19 | 20 | public class Payment 21 | { 22 | [Required] 23 | public decimal Amount { get; set; } 24 | 25 | [Required, RegularExpression("^[3-6]?\\d{12,15}$")] 26 | public string CardNumber { get; set; } 27 | 28 | /// Credit card expiration Month 29 | /// 6 30 | [Required, Range(1, 12)] 31 | public int ExpMonth { get; set; } 32 | 33 | /// Credit card expiration Year 34 | /// 96 35 | [Required, Range(14, 99)] 36 | public int ExpYear { get; set; } 37 | 38 | [StringLength(500, MinimumLength = 10)] 39 | public string Note { get; set; } 40 | 41 | public Guid guid { get; set; } 42 | 43 | [MinLength(2)] 44 | [MaxLength(100)] 45 | public string Detail { get; set; } 46 | 47 | [Required, Range(1.1, 32.9)] 48 | public double Tax { get; set; } 49 | } 50 | } -------------------------------------------------------------------------------- /Swagger.Net/Application/InfoBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Swagger.Net.Application 4 | { 5 | public class InfoBuilder 6 | { 7 | private string _version; 8 | private string _title; 9 | private string _description; 10 | private string _termsOfService; 11 | private readonly ContactBuilder _contactBuilder = new ContactBuilder(); 12 | private readonly LicenseBuilder _licenseBuilder = new LicenseBuilder(); 13 | 14 | public InfoBuilder(string version, string title) 15 | { 16 | _version = version; 17 | _title = title; 18 | } 19 | 20 | public InfoBuilder Description(string description) 21 | { 22 | _description = description; 23 | return this; 24 | } 25 | 26 | public InfoBuilder TermsOfService(string termsOfService) 27 | { 28 | _termsOfService = termsOfService; 29 | return this; 30 | } 31 | 32 | public InfoBuilder Contact(Action contact) 33 | { 34 | contact(_contactBuilder); 35 | return this; 36 | } 37 | 38 | public InfoBuilder License(Action license) 39 | { 40 | license(_licenseBuilder); 41 | return this; 42 | } 43 | 44 | internal Info Build() 45 | { 46 | return new Info 47 | { 48 | version = _version, 49 | title = _title, 50 | description = _description, 51 | termsOfService = _termsOfService, 52 | contact = _contactBuilder.Build(), 53 | license = _licenseBuilder.Build() 54 | }; 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/FromUriParamsController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Web.Http; 5 | 6 | namespace Swagger.Net.Dummy.Controllers 7 | { 8 | public class FromUriParamsController : ApiController 9 | { 10 | [HttpHead] 11 | public bool SupportsCurrencies([FromUri]IEnumerable currencies) 12 | { 13 | throw new NotImplementedException(); 14 | } 15 | 16 | [HttpGet] 17 | public decimal CalculateTax( 18 | int id, 19 | [FromUri(Name="trx")]Transaction transaction, 20 | [FromUri(Name="")]BillingInfo billingInfo) 21 | { 22 | throw new NotImplementedException(); 23 | } 24 | 25 | [HttpGet] 26 | public IEnumerable SearchTransactions([FromUri]TransactionSearch search) 27 | { 28 | throw new NotImplementedException(); 29 | } 30 | } 31 | 32 | public class TransactionSearch 33 | { 34 | public ICollection TransactionIds { get; set; } 35 | } 36 | 37 | public class Transaction 38 | { 39 | public int Id { get; internal set; } 40 | 41 | [Required] 42 | public string Currency { get; set; } 43 | 44 | [Required] 45 | public decimal Amount { get; set; } 46 | } 47 | 48 | public class BillingInfo 49 | { 50 | [Required] 51 | public Address BillTo { get; set; } 52 | 53 | public Address ShipTo { get; set; } 54 | } 55 | 56 | public class Address 57 | { 58 | [Required] 59 | public string Country { get; set; } 60 | 61 | public string City { get; set; } 62 | } 63 | } -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Annotations/SwaggerResponseAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace Swagger.Net.Annotations 5 | { 6 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] 7 | public class SwaggerResponseAttribute : Attribute 8 | { 9 | public SwaggerResponseAttribute(HttpStatusCode statusCode) 10 | { 11 | StatusCode = (int)statusCode; 12 | } 13 | 14 | public SwaggerResponseAttribute(HttpStatusCode statusCode, string description = null, Type type = null, string typeName = null, string mediaType = null, object examples = null) 15 | : this(statusCode) 16 | { 17 | Description = description; 18 | Type = type; 19 | TypeName = typeName; 20 | MediaType = mediaType; 21 | Examples = examples; 22 | } 23 | 24 | public SwaggerResponseAttribute(int statusCode) 25 | { 26 | StatusCode = statusCode; 27 | } 28 | 29 | public SwaggerResponseAttribute(int statusCode, string description = null, Type type = null, string typeName = null, string mediaType = null, object examples = null) 30 | : this(statusCode) 31 | { 32 | Description = description; 33 | Type = type; 34 | TypeName = typeName; 35 | MediaType = mediaType; 36 | Examples = examples; 37 | } 38 | 39 | public int StatusCode { get; private set; } 40 | 41 | public string Description { get; set; } 42 | 43 | public Type Type { get; set; } 44 | 45 | public string TypeName { get; set; } 46 | 47 | public string MediaType { get; set; } 48 | 49 | public object Examples { get; set; } 50 | } 51 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Owin/PredefinedHttpControllerSelector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web.Http; 5 | using System.Web.Http.Controllers; 6 | using System.Web.Http.Dispatcher; 7 | 8 | namespace Swagger.Net.Tests.Owin 9 | { 10 | /// 11 | /// Configures only selected controllers with Asp.Net. 12 | /// Used in setting up owin in memory pipeline with routes only 13 | /// from a predefined list of controllers 14 | /// 15 | public class PredefinedHttpControllerSelector : DefaultHttpControllerSelector 16 | { 17 | private readonly Type[] supportedControllers; 18 | 19 | /// 20 | /// Overrides supportedControllers param and adds all controllers to owin pipeline found in assembly. 21 | /// Set this to true when running tests in CI, and use false when debugging/developing for faster feedback cycle. 22 | /// 23 | private readonly bool forceIncludeAllControllers; 24 | 25 | public PredefinedHttpControllerSelector(HttpConfiguration configuration, Type[] supportedControllers, bool forceIncludeAllControllers) : base(configuration) 26 | { 27 | this.supportedControllers = supportedControllers; 28 | this.forceIncludeAllControllers = forceIncludeAllControllers; 29 | } 30 | 31 | public override IDictionary GetControllerMapping() 32 | { 33 | var m = base.GetControllerMapping(); 34 | 35 | if (forceIncludeAllControllers) 36 | return m; 37 | 38 | return m.Where(y => supportedControllers.Contains(y.Value.ControllerType)) 39 | .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Swagger/SecurityTests2.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NUnit.Framework; 3 | using Swagger.Net.Dummy.Controllers; 4 | using Swagger.Net.Tests.Swagger; 5 | 6 | namespace Swagger.Net.Tests.SwaggerFilters 7 | { 8 | [TestFixture] 9 | public class SecurityTests2 : SwaggerTestBase 10 | { 11 | public SecurityTests2() : base("swagger/docs/{apiVersion}") { } 12 | 13 | [SetUp] 14 | public void SetUp() 15 | { 16 | SetUpDefaultRouteFor(); 17 | } 18 | 19 | [Test] 20 | public void It_exposes_securityDefinitions() 21 | { 22 | SetUpHandler(c => 23 | { 24 | c.ApiKey("token", "header", "TokenAuth", typeof(TokenAuthAttribute)); 25 | }); 26 | 27 | var swagger = GetContent(TEMP_URI.DOCS); 28 | var securityDefinitions = swagger["securityDefinitions"]; 29 | var expected = JObject.FromObject(new 30 | { 31 | token = new 32 | { 33 | type = "apiKey", 34 | description = "TokenAuth", 35 | name = "token", 36 | @in = "header", 37 | }, 38 | }); 39 | 40 | Assert.AreEqual(expected.ToString(), securityDefinitions.ToString()); 41 | } 42 | 43 | [Test] 44 | public void It_exposes_security_on_action() 45 | { 46 | SetUpHandler(c => 47 | { 48 | c.ApiKey("token", "header", "TokenAuth", typeof(TokenAuthAttribute)); 49 | }); 50 | 51 | var swagger = GetContent(TEMP_URI.DOCS); 52 | 53 | Assert.IsNotNull(swagger.paths["/protectedresources2"].get.security); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Swagger/SecurityTests3.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NUnit.Framework; 3 | using Swagger.Net.Dummy.Controllers; 4 | using Swagger.Net.Tests.Swagger; 5 | 6 | namespace Swagger.Net.Tests.SwaggerFilters 7 | { 8 | [TestFixture] 9 | public class SecurityTests3 : SwaggerTestBase 10 | { 11 | public SecurityTests3() : base("swagger/docs/{apiVersion}") { } 12 | 13 | [SetUp] 14 | public void SetUp() 15 | { 16 | SetUpDefaultRouteFor(); 17 | } 18 | 19 | [Test] 20 | public void It_exposes_securityDefinitions() 21 | { 22 | SetUpHandler(c => 23 | { 24 | c.ApiKey("token", "header", "TokenAuth", typeof(TokenClassAuthAttribute)); 25 | }); 26 | 27 | var swagger = GetContent(TEMP_URI.DOCS); 28 | var securityDefinitions = swagger["securityDefinitions"]; 29 | var expected = JObject.FromObject(new 30 | { 31 | token = new 32 | { 33 | type = "apiKey", 34 | description = "TokenAuth", 35 | name = "token", 36 | @in = "header", 37 | }, 38 | }); 39 | 40 | Assert.AreEqual(expected.ToString(), securityDefinitions.ToString()); 41 | } 42 | 43 | [Test] 44 | public void It_exposes_security_on_action() 45 | { 46 | SetUpHandler(c => 47 | { 48 | c.ApiKey("token", "header", "TokenAuth", typeof(TokenClassAuthAttribute)); 49 | }); 50 | 51 | var swagger = GetContent(TEMP_URI.DOCS); 52 | 53 | Assert.IsNotNull(swagger.paths["/protectedresources3"].get.security); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/MetadataAnnotatedTypesController.cs: -------------------------------------------------------------------------------- 1 | using Swagger.Net.Annotations; 2 | using System; 3 | using System.ComponentModel; 4 | using System.ComponentModel.DataAnnotations; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Web.Http; 8 | 9 | namespace Swagger.Net.Dummy.Controllers 10 | { 11 | public class MetadataAnnotatedTypesController : ApiController 12 | { 13 | public int Create(PaymentWithMetadata payment) 14 | { 15 | if (!ModelState.IsValid) 16 | throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); 17 | 18 | throw new NotImplementedException(); 19 | } 20 | 21 | [SwaggerExample("CardNum", "4012888888881881")] 22 | public string Get([RegularExpression("^[3-6]?\\d{12,15}$")]string CardNum) 23 | { 24 | return CardNum; 25 | } 26 | } 27 | 28 | public class PaymentMetadata 29 | { 30 | [Required] 31 | public decimal Amount { get; set; } 32 | 33 | [Required, RegularExpression("^[3-6]?\\d{12,15}$")] 34 | public string CardNumber { get; set; } 35 | 36 | [Required, Range(1, 12)] 37 | public int ExpMonth { get; set; } 38 | 39 | [Required, Range(14, 99)] 40 | public int ExpYear { get; set; } 41 | 42 | [DefaultValue("HelloWorld")] 43 | [StringLength(500, MinimumLength = 10)] 44 | public string Note { get; set; } 45 | } 46 | 47 | [MetadataType(typeof(PaymentMetadata))] 48 | public class PaymentWithMetadata { 49 | public decimal Amount { get; set; } 50 | 51 | public string CardNumber { get; set; } 52 | 53 | public int ExpMonth { get; set; } 54 | 55 | public int ExpYear { get; set; } 56 | 57 | public string Note { get; set; } 58 | } 59 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.WebHost/Web.config: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/ApplyXmlActionCommentsTests.cs: -------------------------------------------------------------------------------- 1 | using Moq; 2 | using NUnit.Framework; 3 | using Swagger.Net.XmlComments; 4 | using System; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Web.Http.Description; 8 | 9 | namespace Swagger.Net.Tests.CoreUnitTests 10 | { 11 | [TestFixture] 12 | class ApplyXmlActionCommentsTests 13 | { 14 | [Test] 15 | public void ApplyXmlActionComments_Empty() 16 | { 17 | Assert.Throws(() => new ApplyXmlActionComments("")); 18 | } 19 | 20 | [Test] 21 | public void ApplyXmlActionComments_Test() 22 | { 23 | string directory = AppDomain.CurrentDomain.BaseDirectory; 24 | string xmlFile = Directory.GetFiles(directory, "*.XML", SearchOption.AllDirectories).FirstOrDefault(); 25 | if (string.IsNullOrEmpty(xmlFile)) 26 | { 27 | Assert.Inconclusive(); 28 | } 29 | else 30 | { 31 | Assert.DoesNotThrow(() => new ApplyXmlActionComments(xmlFile)); 32 | } 33 | } 34 | 35 | [Test] 36 | public void reflectedActionDescriptor_Test() 37 | { 38 | string directory = AppDomain.CurrentDomain.BaseDirectory; 39 | string xmlFile = Directory.GetFiles(directory, "*.XML", SearchOption.AllDirectories).FirstOrDefault(); 40 | var action = new ApplyXmlActionComments(xmlFile); 41 | 42 | var mock = new Mock(); 43 | Assert.DoesNotThrow(() => action.Apply(null, null, mock.Object)); 44 | } 45 | 46 | [Test] 47 | public void ApplyParamComments_Test() 48 | { 49 | var mock = new Mock(); 50 | Assert.DoesNotThrow(() => ApplyXmlActionComments.ApplyParamComments(mock.Object, null, null)); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Swagger.Net/Application/VendorExtensionsConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Serialization; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | namespace Swagger.Net.Application 8 | { 9 | public class VendorExtensionsConverter : JsonConverter 10 | { 11 | public override bool CanConvert(Type objectType) 12 | { 13 | return objectType.GetField("vendorExtensions") != null; 14 | } 15 | 16 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 17 | { 18 | throw new NotImplementedException(); 19 | } 20 | 21 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 22 | { 23 | var jsonContract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType()); 24 | 25 | writer.WriteStartObject(); 26 | 27 | foreach (var jsonProp in jsonContract.Properties) 28 | { 29 | var propValue = jsonProp.ValueProvider.GetValue(value); 30 | if (propValue == null && serializer.NullValueHandling == NullValueHandling.Ignore) 31 | continue; 32 | 33 | if (jsonProp.PropertyName == "vendorExtensions") 34 | { 35 | var vendorExtensions = (IDictionary)propValue; 36 | if (vendorExtensions.Any()) 37 | { 38 | foreach (var entry in vendorExtensions) 39 | { 40 | writer.WritePropertyName(entry.Key); 41 | serializer.Serialize(writer, entry.Value); 42 | } 43 | } 44 | } 45 | else 46 | { 47 | writer.WritePropertyName(jsonProp.PropertyName); 48 | serializer.Serialize(writer, propValue); 49 | } 50 | } 51 | 52 | writer.WriteEndObject(); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Annotations/ApplySwaggerOperationApiKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web.Http.Controllers; 5 | using System.Web.Http.Description; 6 | 7 | namespace Swagger.Net.Annotations 8 | { 9 | public class ApplySwaggerOperationApiKey : IOperationFilter 10 | { 11 | private string _apiKey; 12 | private Type _type; 13 | 14 | public ApplySwaggerOperationApiKey(string apiKey, Type type) 15 | { 16 | _apiKey = apiKey; 17 | _type = type; 18 | } 19 | 20 | public void Apply(Operation o, SchemaRegistry s, ApiDescription a) 21 | { 22 | var addSecurity = a.ActionDescriptor.GetFilterPipeline() 23 | .Select(filterInfo => filterInfo.Instance) 24 | .Where(x => _type.IsInstanceOfType(x)) 25 | .Distinct().Any(); 26 | 27 | if (!addSecurity) 28 | { 29 | var ad = a.ActionDescriptor as ReflectedHttpActionDescriptor; 30 | addSecurity = ad.MethodInfo.CustomAttributes 31 | .Where(x => x.AttributeType.ToString() == _type.ToString()) 32 | .Any(); 33 | } 34 | 35 | if (!addSecurity) 36 | { 37 | addSecurity = a.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(true) 38 | .Where(x => x.ToString() == _type.ToString()) 39 | .Any(); 40 | } 41 | 42 | if (addSecurity) 43 | { 44 | if (o.security == null) 45 | o.security = new List>>(); 46 | else if (o.security.Any(x => x.ContainsKey(_apiKey))) 47 | return; 48 | 49 | var SecRequirements = new Dictionary> 50 | { 51 | { _apiKey, new List() } 52 | }; 53 | o.security.Add(SecRequirements); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Annotations/ApplySwaggerResponseAttributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Net; 4 | using System.Collections.Generic; 5 | using System.Web.Http.Description; 6 | 7 | namespace Swagger.Net.Annotations 8 | { 9 | public class ApplySwaggerResponseAttributes : IOperationFilter 10 | { 11 | public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) 12 | { 13 | if (apiDescription.GetControllerAndActionAttributes().Any()) 14 | operation.responses.Clear(); 15 | 16 | var responseAttributes = apiDescription 17 | .GetControllerAndActionAttributes() 18 | .OrderBy(attr => attr.StatusCode); 19 | foreach (var attr in responseAttributes) 20 | { 21 | var statusCode = attr.StatusCode.ToString(); 22 | 23 | operation.responses[statusCode] = new Response 24 | { 25 | description = attr.Description ?? InferDescriptionFrom(statusCode), 26 | schema = (attr.Type != null) ? schemaRegistry.GetOrRegister(attr.Type, attr.TypeName) : null 27 | }; 28 | if (attr.MediaType != null && attr.Examples != null) 29 | { 30 | operation.responses[statusCode].examples = new Dictionary { { attr.MediaType, attr.Examples } }; 31 | } 32 | } 33 | 34 | var mediaTypes = responseAttributes 35 | .Where(x => !string.IsNullOrEmpty(x.MediaType)) 36 | .Select(x => x.MediaType).ToList(); 37 | if (mediaTypes.Count > 0) 38 | { 39 | operation.produces = mediaTypes; 40 | } 41 | } 42 | 43 | public string InferDescriptionFrom(string statusCode) 44 | { 45 | HttpStatusCode enumValue; 46 | if (Enum.TryParse(statusCode, true, out enumValue)) 47 | { 48 | return enumValue.ToString(); 49 | } 50 | return null; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Swagger-Net 2 | ========= 3 | 4 | [![AppVeyor](https://img.shields.io/appveyor/ci/heldersepu/swagger-net.svg)](https://ci.appveyor.com/project/heldersepu/swagger-net) 5 | 6 | 7 | Seamlessly add [Swagger](http://swagger.io/) to WebApi projects! Combines ApiExplorer and Swagger/swagger-ui(3.x) to provide a rich discovery, documentation and playground experience to your API consumers. 8 | 9 | In addition to its Swagger generator, the [swagger-ui](https://github.com/swagger-api/swagger-ui) is embedded and will automatically serve up once Swagger-Net is installed. This means you can complement your API with a slick discovery UI to assist consumers with their integration efforts. 10 | 11 | **Swagger-Net Features:** 12 | 13 | * Latest and greatest of all dependencies 14 | * High UnitTest CodeCoverage (99%) 15 | * Auto-generated [Swagger 2.0](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md) 16 | * Seamless integration of swagger-ui (3.x) 17 | * Reflection-based Schema generation for describing API types 18 | * Extensibility hooks for customizing the generated Swagger doc and swagger-ui 19 | * Out-of-the-box support for Xml comments 20 | 21 | And that's not all ... 22 | 23 | Once your Web API can describe itself, you've opened the treasure chest of Swagger-based tools including a client generator that can be targeted to a wide range of popular platforms. See [swagger-codegen](https://github.com/swagger-api/swagger-codegen) for more details. 24 | 25 | ___ 26 | 27 | ## [Getting Started](https://github.com/heldersepu/Swagger-Net/wiki/1-Getting-Started) ## 28 | ## [Troubleshooting](https://github.com/heldersepu/Swagger-Net/wiki/2-Troubleshooting) ## 29 | ## [Authorization Schemes](https://github.com/heldersepu/Swagger-Net/wiki/2-Troubleshooting#describing-securityauthorization-schemes) 30 | ## [Customizing the swagger-ui](https://github.com/heldersepu/Swagger-Net/wiki/3-Customizing-the-swagger-ui) ## 31 | ## [Troubleshooting and FAQ's](https://github.com/heldersepu/Swagger-Net/wiki/4-Troubleshooting-and-FAQ's) ## 32 | 33 | ___ 34 | 35 | 36 | ## Donate 37 | 38 | This project is free and will always be. 39 | 40 | If you like it, please support it by making a donation! 41 | 42 | [![donate](https://user-images.githubusercontent.com/30294218/61724877-16fa7a80-ad6f-11e9-80de-9771e0b820ae.png)](https://paypal.me/heldersepu) 43 | -------------------------------------------------------------------------------- /Swagger.Net/Application/SwaggerUiHandler.cs: -------------------------------------------------------------------------------- 1 | using Swagger.Net.SwaggerUi; 2 | using System; 3 | using System.Net; 4 | using System.Net.Http; 5 | using System.Net.Http.Headers; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace Swagger.Net.Application 10 | { 11 | public class SwaggerUiHandler : HttpMessageHandler 12 | { 13 | private readonly SwaggerUiConfig _config; 14 | 15 | public SwaggerUiHandler(SwaggerUiConfig config) 16 | { 17 | _config = config; 18 | } 19 | 20 | protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 21 | { 22 | var swaggerUiProvider = _config.GetSwaggerUiProvider(); 23 | var rootUrl = _config.GetRootUrl(request); 24 | var assetPath = request.GetRouteData().Values["assetPath"].ToString(); 25 | 26 | foreach (var query in request.GetQueryNameValuePairs()) 27 | { 28 | switch (query.Key) 29 | { 30 | case "cssTheme": _config.CssTheme(query.Value); break; 31 | } 32 | } 33 | 34 | try 35 | { 36 | var webAsset = swaggerUiProvider.GetAsset(rootUrl, assetPath.TrimEnd('/')); 37 | var content = ContentFor(webAsset); 38 | return TaskFor(new HttpResponseMessage { Content = content, RequestMessage = request }); 39 | } 40 | catch (AssetNotFound ex) 41 | { 42 | return TaskFor(request.CreateErrorResponse(HttpStatusCode.NotFound, ex)); 43 | } 44 | } 45 | 46 | private HttpContent ContentFor(Asset webAsset) 47 | { 48 | int bufferSize = (int)Math.Min(int.MaxValue, webAsset.Stream.Length); 49 | var content = new StreamContent(webAsset.Stream, bufferSize); 50 | content.Headers.ContentType = new MediaTypeHeaderValue(webAsset.MediaType); 51 | return content; 52 | } 53 | 54 | private Task TaskFor(HttpResponseMessage response) 55 | { 56 | var tsc = new TaskCompletionSource(); 57 | tsc.SetResult(response); 58 | return tsc.Task; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/ApiDescriptionExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using Moq; 2 | using NUnit.Framework; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Net.Http; 6 | using System.Web.Http.Controllers; 7 | using System.Web.Http.Description; 8 | 9 | namespace Swagger.Net.Tests.CoreUnitTests 10 | { 11 | [TestFixture] 12 | class ApiDescriptionExtensionsTests 13 | { 14 | private ApiDescription apiDescription(string path, string method) 15 | { 16 | var mock = new Mock(); 17 | mock.Object.ControllerDescriptor = new HttpControllerDescriptor(); 18 | 19 | return new ApiDescription() 20 | { 21 | RelativePath = path, 22 | HttpMethod = new HttpMethod(method), 23 | ActionDescriptor = mock.Object 24 | }; 25 | } 26 | 27 | [TestCase("_PostAsdf", "asdf", "POST")] 28 | [TestCase("_PostByAsdf", "{asdf", "POST")] 29 | [TestCase("_PostByIdByCo", "{id}/{co}", "POST")] 30 | public void FriendlyId2_Test(string expected, string path, string method) 31 | { 32 | Assert.AreEqual(expected, apiDescription(path, method).FriendlyId2()); 33 | } 34 | 35 | [Test] 36 | public void ResponseType_Null() 37 | { 38 | Assert.IsNull(apiDescription(null, "POST").ResponseType()); 39 | } 40 | 41 | [Test] 42 | public void ResponseType_Empty() 43 | { 44 | var ad = new ApiDescription(); 45 | Assert.Throws(() => ad.ResponseType()); 46 | } 47 | 48 | [Test] 49 | public void GetUniqueFriendlyId_Test() 50 | { 51 | var apiDesc = apiDescription("asdf", "POST"); 52 | string friendlyId = apiDesc.FriendlyId(); 53 | string friendlyId2 = apiDesc.FriendlyId2(); 54 | Assert.AreNotEqual(friendlyId, friendlyId2); 55 | 56 | var operationNames = new HashSet { friendlyId, friendlyId2 }; 57 | var swaggerGenerator = new SwaggerGenerator(null, null, null); 58 | string uniqueFriendlyId = swaggerGenerator.GetUniqueOperationId(apiDesc, operationNames); 59 | Assert.AreNotEqual(friendlyId2, uniqueFriendlyId); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Owin/InMemoryOwinTest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NUnit.Framework; 3 | using System.Net; 4 | using System.Net.Http; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Web.Http; 8 | 9 | namespace Swagger.Net.Tests.Owin 10 | { 11 | [TestFixture] 12 | public class InMemoryOwinTest : BaseInMemoryOwinSwaggerTest 13 | { 14 | [RoutePrefix("v1/callback")] 15 | [AllowAnonymous] 16 | public class CallbackController : ApiController 17 | { 18 | [Route, HttpGet] 19 | public HttpResponseMessage Get() 20 | { 21 | var response = new HttpResponseMessage(HttpStatusCode.OK); 22 | response.Content = new StringContent("{key:1}", Encoding.UTF8, "application/json"); 23 | return response; 24 | } 25 | } 26 | 27 | [Test] 28 | public async Task It_supports_configuring_single_swagger_endpoint() 29 | { 30 | // Given 31 | UseInMemoryOwinServer(app => new OwinStartup(typeof(CallbackController)).Configuration(app)); 32 | 33 | // When 34 | var swagger = await GetSwaggerDocs(); 35 | 36 | // Then 37 | var postResponses = swagger["paths"]["/v1/callback"]["get"]["operationId"]; 38 | 39 | Assert.That(postResponses.Value(), Is.EqualTo("Callback_Get")); 40 | } 41 | 42 | [Test] 43 | public async Task It_supports_configuring_multiple_swagger_endpoints() 44 | { 45 | // Given 46 | UseInMemoryOwinServer(app => new MultiSwaggerOwinStartup(typeof(CallbackController)).Configuration(app)); 47 | 48 | // When, 49 | var swaggerRoot = await GetSwaggerDocs(); 50 | 51 | // Then 52 | var postResponses = swaggerRoot["paths"]["/v1/callback"]["get"]["operationId"]; 53 | Assert.That(postResponses.Value(), Is.EqualTo("Callback_Get")); 54 | 55 | // When, 56 | var swaggerCustom = await GetContent("docs/v1/.metadata"); 57 | 58 | // Then 59 | postResponses = swaggerCustom["paths"]["/v1/callback"]["get"]["operationId"]; 60 | Assert.That(postResponses.Value(), Is.EqualTo("Callback_Get")); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Extensions/JsonPropertyExtensions.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Serialization; 3 | using System; 4 | using System.ComponentModel.DataAnnotations; 5 | using System.Linq; 6 | using System.Reflection; 7 | 8 | namespace Swagger.Net 9 | { 10 | public static class JsonPropertyExtensions 11 | { 12 | public static bool IsRequired(this JsonProperty jsonProperty) 13 | { 14 | return jsonProperty.HasAttribute() || jsonProperty.Required == Required.Always; 15 | } 16 | 17 | public static bool IsObsolete(this JsonProperty jsonProperty) 18 | { 19 | return jsonProperty.HasAttribute(); 20 | } 21 | 22 | public static bool HasAttribute(this JsonProperty jsonProperty) 23 | { 24 | var propInfo = jsonProperty.PropertyInfo(); 25 | if (propInfo != null) 26 | { 27 | return Attribute.IsDefined(propInfo, typeof(T)); 28 | } 29 | else 30 | { 31 | var fieldInfo = jsonProperty.FieldInfo(); 32 | return fieldInfo != null && Attribute.IsDefined(fieldInfo, typeof(T)); ; 33 | } 34 | } 35 | 36 | public static PropertyInfo PropertyInfo(this JsonProperty jsonProperty) 37 | { 38 | if (jsonProperty.UnderlyingName == null) return null; 39 | 40 | var metadata = jsonProperty.DeclaringType.GetCustomAttributes(typeof(MetadataTypeAttribute), true) 41 | .FirstOrDefault(); 42 | 43 | var typeToReflect = (metadata != null) 44 | ? ((MetadataTypeAttribute)metadata).MetadataClassType 45 | : jsonProperty.DeclaringType; 46 | 47 | return typeToReflect.GetProperty(jsonProperty.UnderlyingName, jsonProperty.PropertyType); 48 | } 49 | 50 | public static FieldInfo FieldInfo(this JsonProperty jsonProperty) 51 | { 52 | if (jsonProperty.UnderlyingName == null) return null; 53 | 54 | var metadata = jsonProperty.DeclaringType.GetCustomAttributes(typeof(MetadataTypeAttribute), true) 55 | .FirstOrDefault(); 56 | 57 | var typeToReflect = (metadata != null) 58 | ? ((MetadataTypeAttribute)metadata).MetadataClassType 59 | : jsonProperty.DeclaringType; 60 | 61 | return typeToReflect.GetField(jsonProperty.UnderlyingName); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Swagger/DefaultRootUrlResolverTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Swagger.Net.Application; 3 | using System.Net.Http; 4 | using System.Web.Http; 5 | using System.Web.Http.Hosting; 6 | 7 | namespace Swagger.Net.Tests.Swagger 8 | { 9 | [TestFixture] 10 | public class DefaultRootUrlResolverTests 11 | { 12 | [Test] 13 | public void It_provides_scheme_host_and_port_from_request_uri() 14 | { 15 | var request = GetRequestFixtureFor(HttpMethod.Get, "http://tempuri.org:1234"); 16 | 17 | var rootUrl = SwaggerDocsConfig.DefaultRootUrlResolver(request); 18 | 19 | Assert.AreEqual("http://tempuri.org:1234", rootUrl); 20 | } 21 | 22 | [Test] 23 | public void It_provides_scheme_host_and_port_from_x_forwarded() 24 | { 25 | var request = GetRequestFixtureFor(HttpMethod.Get, "http://tempuri.org:1234"); 26 | request.Headers.Add("X-Forwarded-Proto", "https"); 27 | request.Headers.Add("X-Forwarded-Host", "acmecorp.org"); 28 | request.Headers.Add("X-Forwarded-Port", "8080"); 29 | request.Headers.Add("X-Forwarded-Prefix", "/api"); 30 | var rootUrl = SwaggerDocsConfig.DefaultRootUrlResolver(request); 31 | 32 | Assert.AreEqual("https://acmecorp.org:8080/api", rootUrl); 33 | } 34 | 35 | [TestCase("http://tempuri.org", "http://tempuri.org")] 36 | [TestCase("http://tempuri.org:80", "http://tempuri.org")] 37 | [TestCase("http://tempuri.org:1234", "http://tempuri.org:1234")] 38 | [TestCase("https://tempuri.org", "https://tempuri.org")] 39 | [TestCase("https://tempuri.org:443", "https://tempuri.org")] 40 | [TestCase("https://tempuri.org:1234", "https://tempuri.org:1234")] 41 | public void It_provides_scheme_and_host_but_omits_default_port_from_request_uri(string requestedUri, string expectedUri) 42 | { 43 | var request = GetRequestFixtureFor(HttpMethod.Get, requestedUri); 44 | 45 | var rootUrl = SwaggerDocsConfig.DefaultRootUrlResolver(request); 46 | 47 | Assert.AreEqual(expectedUri, rootUrl); 48 | } 49 | 50 | private HttpRequestMessage GetRequestFixtureFor(HttpMethod method, string requestUri) 51 | { 52 | var fixture = new HttpRequestMessage(method, requestUri); 53 | fixture.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration()); 54 | return fixture; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Owin/BaseInMemoryOwinSwaggerTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Owin.Testing; 2 | using Newtonsoft.Json.Linq; 3 | using NUnit.Framework; 4 | using Owin; 5 | using System; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | 9 | namespace Swagger.Net.Tests.Owin 10 | { 11 | [TestFixture] 12 | public abstract class BaseInMemoryOwinSwaggerTest 13 | { 14 | protected HttpClient _client; 15 | private IDisposable _server; 16 | 17 | /// 18 | /// Generates swagger documentation only for specific controllers. 19 | /// 20 | /// 21 | protected void UseInMemoryOwinServer(params Type[] controllerType) 22 | { 23 | ConfigureInMemoryOwinServer(appBuilder => ConfigureOwinStartup(controllerType, appBuilder)); 24 | } 25 | 26 | protected void UseInMemoryOwinServer(Action owinStartupBuilder) 27 | { 28 | ConfigureInMemoryOwinServer(appBuilder => owinStartupBuilder(appBuilder)); 29 | } 30 | 31 | private void ConfigureOwinStartup(Type[] controller, IAppBuilder appBuilder) 32 | { 33 | new OwinStartup(controller).Configuration(appBuilder); 34 | } 35 | 36 | private void ConfigureInMemoryOwinServer(Action appBuilder) 37 | { 38 | var testServer = TestServer.Create(appBuilder); 39 | _client = testServer.HttpClient; 40 | _server = testServer; 41 | } 42 | 43 | [OneTimeTearDown] 44 | public void TeardownFixture() 45 | { 46 | if (_server != null) _server.Dispose(); 47 | _server = null; 48 | } 49 | 50 | [TearDown] 51 | public void Teardown() 52 | { 53 | if (_server != null) _server.Dispose(); 54 | _server = null; 55 | } 56 | 57 | protected async Task GetSwaggerDocs() 58 | { 59 | return await GetContent("swagger/docs/v1"); 60 | } 61 | 62 | protected async Task GetContent(string uri) 63 | { 64 | var response = await _client.GetAsync(uri); 65 | if (response.IsSuccessStatusCode == false) 66 | Assert.Fail("Failed request to {0}: Status code: {1} {2}", uri, response.StatusCode.ToString(), response.ReasonPhrase); 67 | var content = await response.Content.ReadAsAsync(); 68 | return content; 69 | 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.Core/Controllers/SwaggerAnnotatedController.cs: -------------------------------------------------------------------------------- 1 | using Swagger.Net.Annotations; 2 | using Swagger.Net.Dummy.SwaggerExtensions; 3 | using Swagger.Net.Swagger.Annotations; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Net; 7 | using System.Net.Http; 8 | using System.Web.Http; 9 | 10 | namespace Swagger.Net.Dummy.Controllers 11 | { 12 | [SwaggerResponse(400, "Bad request")] 13 | public class SwaggerAnnotatedController : ApiController 14 | { 15 | [SwaggerResponseRemoveDefaults] 16 | [SwaggerResponse(HttpStatusCode.Created, Type = typeof(int), MediaType = "text", Examples = 123)] 17 | [SwaggerResponse(HttpStatusCode.BadRequest, "Invalid message", typeof(HttpError))] 18 | public int Create(Message message) 19 | { 20 | throw new NotImplementedException(); 21 | } 22 | 23 | [SwaggerResponse( 200, Type = typeof( IEnumerable ), TypeName = "Messages" )] 24 | public IEnumerable GetAll() 25 | { 26 | throw new NotImplementedException(); 27 | } 28 | 29 | /// 30 | /// Get message by Id 31 | /// 32 | /// The id 33 | /// The message. 34 | /// Not found 35 | [SwaggerResponse(HttpStatusCode.OK, Type = typeof(Message))] 36 | [SwaggerOperationFilter(typeof(AddGetMessageExamples))] 37 | public Message GetById(int id) 38 | { 39 | throw new NotImplementedException(); 40 | } 41 | 42 | [SwaggerResponse(200, MediaType = "image/png")] 43 | public HttpResponseMessage Patch() 44 | { 45 | throw new NotImplementedException(); 46 | } 47 | 48 | [HttpPut] 49 | [SwaggerOperation("UpdateMessage", Tags = new[] { "messages" }, Schemes = new[] { "ws" })] 50 | public void Put([SwaggerDescription("param description")]int id, Message message) 51 | { 52 | throw new NotImplementedException(); 53 | } 54 | 55 | public void Delete(Message2 message) 56 | { 57 | throw new NotImplementedException(); 58 | } 59 | } 60 | 61 | public class Message2: Message { } 62 | 63 | [SwaggerSchemaFilter(typeof(AddMessageDefault))] 64 | public class Message 65 | { 66 | [SwaggerDescription("param model description")] 67 | public string Title { get; set; } 68 | public string Content { get; set; } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/XPathNavigatorExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Swagger.Net.XmlComments; 3 | using System.IO; 4 | using System.Text.RegularExpressions; 5 | using System.Xml.XPath; 6 | 7 | namespace Swagger.Net.Tests.CoreUnitTests 8 | { 9 | [TestFixture] 10 | class XPathNavigatorExtensionsTests 11 | { 12 | [Test] 13 | public void XPathNavigator_null() 14 | { 15 | XPathNavigator navigator = null; 16 | string content = navigator.ExtractContent(); 17 | Assert.AreEqual(null, content); 18 | } 19 | 20 | [Test] 21 | public void XPathNavigator_reads_empty_doc() 22 | { 23 | string xml = ""; 24 | XPathDocument xmlDoc; 25 | using (StringReader stream = new StringReader(xml)) 26 | { 27 | xmlDoc = new XPathDocument(stream); 28 | } 29 | XPathNavigator navigator = xmlDoc.CreateNavigator(); 30 | string content = navigator.ExtractContent(); 31 | Assert.AreEqual(xml, content); 32 | } 33 | 34 | [Test] 35 | public void XPathNavigator_reads_ConstPattern() 36 | { 37 | string xml = "testonetesttwo"; 38 | XPathDocument xmlDoc; 39 | using (StringReader stream = new StringReader(xml)) 40 | { 41 | xmlDoc = new XPathDocument(stream); 42 | } 43 | XPathNavigator navigator = xmlDoc.CreateNavigator(); 44 | string content = navigator.ExtractContent(); 45 | Assert.AreEqual(xml.Replace("", "").Replace("", ""), content.Strip()); 46 | } 47 | 48 | [TestCase(null, "")] 49 | [TestCase(null, "asdf")] 50 | [TestCase("abc", "abc")] 51 | public void GetConstRefName(string expected, string input) 52 | { 53 | Regex rx = XPathNavigatorExtensions.ConstPattern; 54 | Match m = rx.Match(input); 55 | string content = XPathNavigatorExtensions.GetConstRefName(m); 56 | Assert.AreEqual(expected, content); 57 | } 58 | 59 | [TestCase(null, "")] 60 | [TestCase(null, "asdf")] 61 | [TestCase("{abc}", @"abc"" />")] 62 | public void GetParamRefName(string expected, string input) 63 | { 64 | Regex rx = XPathNavigatorExtensions.ParamPattern; 65 | Match m = rx.Match(input); 66 | string content = XPathNavigatorExtensions.GetParamRefName(m); 67 | Assert.AreEqual(expected, content); 68 | } 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Swagger.Net/Swagger/Extensions/TypeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using System.Runtime.Serialization; 5 | using System.Text; 6 | 7 | namespace Swagger.Net 8 | { 9 | public static class TypeExtensions 10 | { 11 | public static string FriendlyId(this Type type, bool fullyQualified = false) 12 | { 13 | var typeName = fullyQualified 14 | ? type.FullNameSansTypeParameters().Replace("+", ".") 15 | : type.Name; 16 | 17 | if (type.IsGenericType) 18 | { 19 | var genericArgumentIds = type.GetGenericArguments() 20 | .Select(t => t.FriendlyId(fullyQualified)) 21 | .ToArray(); 22 | 23 | return new StringBuilder(typeName) 24 | .Replace(string.Format("`{0}", genericArgumentIds.Count()), string.Empty) 25 | .Append(string.Format("Of{0}", string.Join("And", genericArgumentIds))) 26 | .ToString(); 27 | } 28 | 29 | return typeName; 30 | } 31 | 32 | public static string FullNameSansTypeParameters(this Type type) 33 | { 34 | var fullName = type.FullName; 35 | if (string.IsNullOrEmpty(fullName)) 36 | fullName = type.Name; 37 | var chopIndex = fullName.IndexOf("[["); 38 | return (chopIndex == -1) ? fullName : fullName.Substring(0, chopIndex); 39 | } 40 | 41 | public static string[] GetEnumNamesForSerialization(this Type enumType, bool excludeObsolete = false) 42 | { 43 | return enumType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static) 44 | .Where(fieldInfo => !excludeObsolete || !fieldInfo.GetCustomAttributes().Any()) 45 | .Select(fieldInfo => 46 | { 47 | var memberAttribute = fieldInfo.GetCustomAttributes(false).OfType().FirstOrDefault(); 48 | return (memberAttribute == null || string.IsNullOrWhiteSpace(memberAttribute.Value)) 49 | ? fieldInfo.Name 50 | : memberAttribute.Value; 51 | }) 52 | .ToArray(); 53 | } 54 | 55 | public static object[] GetEnumValuesForSerialization(this Type enumType, bool excludeObsolete = false) 56 | { 57 | return enumType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static) 58 | .Where(fieldInfo => !excludeObsolete || !fieldInfo.GetCustomAttributes().Any()) 59 | .Select(fieldInfo => fieldInfo.GetRawConstantValue()) 60 | .ToArray(); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /Swagger.Net/Swagger/XmlComments/XmlTextHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Swagger.Net.XmlComments 5 | { 6 | public static class XmlTextHelper 7 | { 8 | /// 9 | /// Messages text from an XML node produced by Visual Studio into a plainer plain text (leading whitespace normalized) 10 | /// 11 | /// The content of an XML node - could contain other XML elements within the string 12 | /// 13 | public static string NormalizeIndentation(string xmlText) 14 | { 15 | if (null == xmlText) 16 | throw new ArgumentNullException("xmlText"); 17 | 18 | string[] lines = xmlText.Split('\n'); 19 | string padding = GetCommonLeadingWhitespace(lines); 20 | 21 | int padLen = padding == null ? 0 : padding.Length; 22 | 23 | // remove leading padding from each line 24 | for (int i = 0, l = lines.Length; i < l; ++i) 25 | { 26 | string line = lines[i].TrimEnd('\r'); // remove trailing '\r' 27 | 28 | if (padLen != 0 && line.Length >= padLen && line.Substring(0, padLen) == padding) 29 | line = line.Substring(padLen); 30 | 31 | lines[i] = line; 32 | } 33 | 34 | // remove leading empty lines, but not all leading padding 35 | // remove all trailing whitespace, regardless 36 | return string.Join("\r\n", lines.SkipWhile(x => string.IsNullOrWhiteSpace(x))).TrimEnd(); 37 | } 38 | 39 | /// 40 | /// Finds the common padding prefix used on all non-empty lines. 41 | /// 42 | /// 43 | /// The common padding found on all non-blank lines - returns null when no common prefix is found 44 | public static string GetCommonLeadingWhitespace(string[] lines) 45 | { 46 | if (null == lines) 47 | throw new ArgumentException("lines"); 48 | 49 | if (lines.Length == 0) 50 | return null; 51 | 52 | string[] nonEmptyLines = lines 53 | .Where(x => !string.IsNullOrWhiteSpace(x)) 54 | .ToArray(); 55 | 56 | if (nonEmptyLines.Length < 1) 57 | return null; 58 | 59 | int padLen = 0; 60 | 61 | // use the first line as a seed, and see what is shared over all nonEmptyLines 62 | string seed = nonEmptyLines[0]; 63 | for (int i = 0, l = seed.Length; i < l; ++i) 64 | { 65 | if (!char.IsWhiteSpace(seed, i)) 66 | break; 67 | 68 | if (nonEmptyLines.Any(line => line[i] != seed[i])) 69 | break; 70 | 71 | ++padLen; 72 | } 73 | 74 | if (padLen > 0) 75 | return seed.Substring(0, padLen); 76 | 77 | return null; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Owin/OwinStartup.cs: -------------------------------------------------------------------------------- 1 | using Owin; 2 | using Swagger.Net.Application; 3 | using System; 4 | using System.Linq; 5 | using System.Web.Http; 6 | using System.Web.Http.Dispatcher; 7 | using System.Web.Http.Routing; 8 | 9 | namespace Swagger.Net.Tests.Owin 10 | { 11 | public class OwinStartup 12 | { 13 | private Type[] supportedControllers = { }; 14 | //private Func> operationIdFunc; 15 | 16 | /// 17 | /// When developing, it is quicker to run tests scoped to just a list of controllers. 18 | /// When running tests in CI, set this to true. 19 | /// 20 | /// Setting this to true ensures that swagger config generation is tested to work in tandem with all controllers, 21 | /// not just scoped to single controllers. 22 | /// 23 | public static bool ForceIncludeAllControllers = false; 24 | 25 | public OwinStartup(params Type[] supportedControllers) 26 | { 27 | this.supportedControllers = supportedControllers; 28 | } 29 | 30 | public void Configuration(IAppBuilder app) 31 | { 32 | if (supportedControllers == null) 33 | supportedControllers = new Type[] { }; 34 | 35 | var config = new HttpConfiguration(); 36 | 37 | config.Services.Replace(typeof(IHttpControllerSelector), new PredefinedHttpControllerSelector(config, supportedControllers, ForceIncludeAllControllers)); 38 | 39 | //Route Constraints 40 | var constraintResolver = new DefaultInlineConstraintResolver(); 41 | 42 | config.MapHttpAttributeRoutes(constraintResolver); 43 | 44 | config.Routes.MapHttpRoute( 45 | name: "DefaultApi", 46 | routeTemplate: "api/{controller}/{id}", 47 | defaults: new { id = System.Web.Http.RouteParameter.Optional } // optional id 48 | ); 49 | 50 | config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; 51 | 52 | ConfigureFormatters(config); 53 | 54 | config.EnsureInitialized(); 55 | 56 | EnableSwagger(config); 57 | 58 | app.UseWebApi(config); 59 | } 60 | 61 | protected virtual void EnableSwagger(HttpConfiguration config) 62 | { 63 | config 64 | .EnableSwagger(c => c.SingleApiVersion("v1", "A title for your API")) 65 | .EnableSwaggerUi(); 66 | } 67 | 68 | protected virtual void ConfigureFormatters(HttpConfiguration config) 69 | { 70 | // remove application/x-www-form-urlencoded formatters 71 | var mediaTypeFormatters = config.Formatters.Where(y => y.SupportedMediaTypes.Any(c => c.MediaType == "application/x-www-form-urlencoded")).ToList(); 72 | mediaTypeFormatters.ForEach(x => config.Formatters.Remove(x)); 73 | 74 | //Xml Formatter 75 | config.Formatters.Remove(config.Formatters.XmlFormatter); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/XmlCommentsIdHelperTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Swagger.Net.Dummy.Controllers; 3 | using Swagger.Net.XmlComments; 4 | 5 | namespace Swagger.Net.Tests.CoreUnitTests 6 | { 7 | [TestFixture] 8 | class XmlCommentsIdHelperTests 9 | { 10 | const string CTRLR = "M:Swagger.Net.Dummy.Controllers."; 11 | 12 | [Test] 13 | public void Method_from_inherited_action_post() 14 | { 15 | var methodInfo = typeof(BaseChildController).GetMethod("Post"); 16 | var comment = methodInfo.GetCommentIdForMethod(); 17 | Assert.AreEqual(CTRLR + "BaseController`1.Post(System.String)", comment); 18 | } 19 | 20 | [Test] 21 | public void Method_from_inherited_action_put() 22 | { 23 | var methodInfo = typeof(BaseChildController).GetMethod("Put"); 24 | var comment = methodInfo.GetCommentIdForMethod(); 25 | Assert.AreEqual(CTRLR + "BaseController`1.Put(`0,System.String)", comment); 26 | } 27 | 28 | [Test] 29 | public void Method_from_generic_action_post() 30 | { 31 | var methodInfo = typeof(BlobController).GetMethod("Post"); 32 | var comment = methodInfo.GetCommentIdForMethod(); 33 | Assert.AreEqual(CTRLR + "Blob`3.Post(System.Int32)", comment); 34 | } 35 | 36 | [Test] 37 | public void Method_from_generic_action_get() 38 | { 39 | var methodInfo = typeof(BlobController).GetMethod("Get"); 40 | var comment = methodInfo.GetCommentIdForMethod(); 41 | Assert.AreEqual(CTRLR + "Blob`3.Get(System.Nullable{System.Int32})", comment); 42 | } 43 | 44 | [Test] 45 | public void Method_from_generic_action_patch() 46 | { 47 | var methodInfo = typeof(BlobController).GetMethod("Patch"); 48 | var comment = methodInfo.GetCommentIdForMethod(); 49 | Assert.AreEqual(CTRLR + "Blob`3.Patch(Swagger.Net.Dummy.Controllers.AnotherFoo{`1},System.Collections.Generic.IEnumerable{`2})", comment); 50 | } 51 | 52 | [Test] 53 | public void Method_from_generic_action_put() 54 | { 55 | var methodInfo = typeof(BlobController).GetMethod("Put"); 56 | var comment = methodInfo.GetCommentIdForMethod(); 57 | Assert.AreEqual(CTRLR + "Blob`3.Put(Swagger.Net.Dummy.Controllers.AnotherFoo{`0})", comment); 58 | } 59 | 60 | [Test] 61 | public void Method_from_NestedEnum_action_get() 62 | { 63 | var methodInfo = typeof(NestedEnumController).GetMethod("Get"); 64 | var comment = methodInfo.GetCommentIdForMethod(); 65 | Assert.AreEqual(CTRLR + "NestedEnum`1.Get(System.Nullable{Swagger.Net.Dummy.Controllers.NestedEnum{`0}.Giorno})", comment); 66 | } 67 | 68 | [Test] 69 | public void Method_from_NestedEnum_action_put() 70 | { 71 | var methodInfo = typeof(NestedEnumController).GetMethod("Put"); 72 | var comment = methodInfo.GetCommentIdForMethod(); 73 | Assert.AreEqual(CTRLR + "NestedEnum`1.Put(Swagger.Net.Dummy.Controllers.NestedEnum{`0}.Giorno)", comment); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/Swagger/TypeExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using Moq; 2 | using NUnit.Framework; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Runtime.Serialization; 6 | 7 | namespace Swagger.Net.Swagger 8 | { 9 | public class TypeExtensionsTests 10 | { 11 | [TestCase(typeof(DateTime), "DateTime")] 12 | [TestCase(typeof(IEnumerable), "IEnumerableOfString")] 13 | [TestCase(typeof(IDictionary), "IDictionaryOfStringAndDecimal")] 14 | public void FriendlyId_ReturnsNonQualifiedFriendlyId_IfFullyQualifiedFlagIsUnset( 15 | Type systemType, 16 | string expectedReturnValue) 17 | { 18 | Assert.AreEqual(expectedReturnValue, systemType.FriendlyId()); 19 | } 20 | 21 | [TestCase(typeof(DateTime), "System.DateTime")] 22 | [TestCase(typeof(IEnumerable), "System.Collections.Generic.IEnumerableOfSystem.String")] 23 | [TestCase(typeof(IDictionary), "System.Collections.Generic.IDictionaryOfSystem.StringAndSystem.Decimal")] 24 | [TestCase(typeof(TypeExtensionsTests.InnerType), "Swagger.Net.Swagger.TypeExtensionsTests.InnerType")] 25 | public void FriendlyId_ReturnsFullQualifiedFriendlyId_IfFullyQualifiedFlagIsSet( 26 | Type systemType, 27 | string expectedReturnValue) 28 | { 29 | Assert.AreEqual(expectedReturnValue, systemType.FriendlyId(true)); 30 | } 31 | 32 | [Test] 33 | public void GetEnumNamesForSerialization_HonorsEnumMemberAttributes() 34 | { 35 | var enumNames = typeof(EnumWithMemberAttributes).GetEnumNamesForSerialization(); 36 | CollectionAssert.AreEqual(new[] { "value-1", "value-2" }, enumNames); 37 | } 38 | 39 | [Test] 40 | public void GetEnumNamesForSerialization_ExcludesObsoleteAttributes() 41 | { 42 | var enumNames = typeof(EnumWithObsoleteAttributes).GetEnumNamesForSerialization(excludeObsolete: true); 43 | CollectionAssert.AreEqual(new[] { "Value3" }, enumNames); 44 | } 45 | 46 | [Test] 47 | public void GetEnumValuesForSerialization_ExcludesObsoleteAttributes() 48 | { 49 | var enumValues = typeof(EnumWithObsoleteAttributes).GetEnumValuesForSerialization(excludeObsolete: true); 50 | CollectionAssert.AreEqual(new[] { 2 }, enumValues); 51 | } 52 | 53 | [Test] 54 | public void FullNameSansTypeParameters_Test() 55 | { 56 | var mock = new Mock(); 57 | mock.Setup(x => x.Name).Returns(""); 58 | Assert.AreEqual("", mock.Object.FullNameSansTypeParameters()); 59 | } 60 | 61 | private class InnerType 62 | { 63 | public string Property1 { get; set; } 64 | } 65 | 66 | public enum EnumWithMemberAttributes 67 | { 68 | [EnumMember(Value = "value-1")] 69 | Value1 = 0, 70 | 71 | [EnumMember(Value = "value-2")] 72 | Value2 = 1 73 | } 74 | 75 | public enum EnumWithObsoleteAttributes 76 | { 77 | [Obsolete] 78 | Value1 = 0, 79 | 80 | [Obsolete] 81 | Value2 = 1, 82 | 83 | Value3 = 2 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/CoreUnitTests/EmbeddedAssetProviderTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Swagger.Net.SwaggerUi; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Swagger.Net.Tests.CoreUnitTests 7 | { 8 | [TestFixture] 9 | class EmbeddedAssetProviderTests 10 | { 11 | [Test] 12 | public void GetAsset_null() 13 | { 14 | var prov = new EmbeddedAssetProvider(null, null); 15 | Assert.Throws(() => prov.GetAsset("", "")); 16 | } 17 | 18 | [Test] 19 | public void GetAsset_empty() 20 | { 21 | var prov = new EmbeddedAssetProvider( 22 | new Dictionary(), 23 | new Dictionary()); 24 | Assert.Throws(() => prov.GetAsset("","")); 25 | } 26 | 27 | [Test] 28 | public void GetAsset_ext_null() 29 | { 30 | var prov = new EmbeddedAssetProvider(null, null); 31 | Assert.Throws(() => prov.GetAsset("", "index/ext")); 32 | } 33 | 34 | [Test] 35 | public void GetAsset_ext_empty() 36 | { 37 | var prov = new EmbeddedAssetProvider( 38 | new Dictionary(), 39 | new Dictionary()); 40 | var a = prov.GetAsset("", "index/ext"); 41 | Assert.AreEqual("application/json", a.MediaType); 42 | Assert.AreEqual(2, a.Stream.Length); 43 | } 44 | 45 | [Test] 46 | public void GetAsset_ext_notnull() 47 | { 48 | var prov = new EmbeddedAssetProvider( 49 | new Dictionary { { "a", null } }, 50 | new Dictionary()); 51 | var a = prov.GetAsset("", "index/ext"); 52 | Assert.AreEqual("application/json", a.MediaType); 53 | Assert.AreEqual(5, a.Stream.Length); 54 | } 55 | 56 | [Test] 57 | public void GetEmbeddedResourceStreamFor_null() 58 | { 59 | var prov = new EmbeddedAssetProvider( 60 | new Dictionary(), 61 | new Dictionary()); 62 | var ad = new EmbeddedAssetDescriptor(typeof(EmbeddedAssetProviderTests).Assembly, "123", false); 63 | Assert.Throws(() => prov.GetEmbeddedResourceStreamFor(ad, "")); 64 | } 65 | 66 | [TestCase("file.css", "text/css")] 67 | [TestCase("file.js", "text/javascript")] 68 | [TestCase("file.gif", "image/gif")] 69 | [TestCase("file.png", "image/png")] 70 | [TestCase("file.eot", "application/vnd.ms-fontobject")] 71 | [TestCase("file.woff", "application/font-woff")] 72 | [TestCase("file.woff2", "application/font-woff2")] 73 | [TestCase("file.otf", "application/font-sfnt")] 74 | [TestCase("file.ttf", "application/font-sfnt")] 75 | [TestCase("file.svg", "image/svg+xml")] 76 | [TestCase("file.json", "application/json")] 77 | [TestCase("file.unkown", "text/html")] 78 | public void InferMediaTypeFrom(string file, string mediaType) 79 | { 80 | Assert.AreEqual(mediaType, EmbeddedAssetProvider.InferMediaTypeFrom(file)); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Swagger.Net/Application/SecuritySchemeBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Web.Http; 4 | 5 | namespace Swagger.Net.Application 6 | { 7 | public abstract class SecuritySchemeBuilder 8 | { 9 | internal abstract SecurityScheme Build(); 10 | } 11 | 12 | public class BasicAuthSchemeBuilder : SecuritySchemeBuilder 13 | { 14 | private string _description; 15 | 16 | public BasicAuthSchemeBuilder Description(string description) 17 | { 18 | _description = description; 19 | return this; 20 | } 21 | 22 | internal override SecurityScheme Build() 23 | { 24 | return new SecurityScheme 25 | { 26 | type = "basic", 27 | description = _description 28 | }; 29 | } 30 | } 31 | 32 | public class ApiKeySchemeBuilder : SecuritySchemeBuilder 33 | { 34 | public string description; 35 | public string name; 36 | public string @in; 37 | public Type type; 38 | 39 | public ApiKeySchemeBuilder(string name, string @in, string description, Type type) 40 | { 41 | this.description = description; 42 | this.name = name; 43 | this.@in = @in; 44 | this.type = type; 45 | } 46 | 47 | internal override SecurityScheme Build() 48 | { 49 | return new SecurityScheme 50 | { 51 | type = "apiKey", 52 | description = description, 53 | name = name, 54 | @in = @in 55 | }; 56 | } 57 | } 58 | 59 | public class OAuth2SchemeBuilder : SecuritySchemeBuilder 60 | { 61 | private string _description; 62 | private string _flow; 63 | private string _authorizationUrl; 64 | private string _tokenUrl; 65 | private IDictionary _scopes = new Dictionary(); 66 | 67 | public OAuth2SchemeBuilder Description(string description) 68 | { 69 | _description = description; 70 | return this; 71 | } 72 | 73 | public OAuth2SchemeBuilder Flow(string flow) 74 | { 75 | _flow = flow; 76 | return this; 77 | } 78 | 79 | public OAuth2SchemeBuilder AuthorizationUrl(string authorizationUrl) 80 | { 81 | _authorizationUrl = authorizationUrl; 82 | return this; 83 | } 84 | 85 | public OAuth2SchemeBuilder TokenUrl(string tokenUrl) 86 | { 87 | _tokenUrl = tokenUrl; 88 | return this; 89 | } 90 | 91 | public OAuth2SchemeBuilder Scopes(Action> configure) 92 | { 93 | configure(_scopes); 94 | return this; 95 | } 96 | 97 | internal override SecurityScheme Build() 98 | { 99 | // TODO: Validate required fields for given flow 100 | 101 | return new SecurityScheme 102 | { 103 | type = "oauth2", 104 | flow = _flow, 105 | authorizationUrl = _authorizationUrl, 106 | tokenUrl = _tokenUrl, 107 | scopes = _scopes, 108 | description = _description, 109 | }; 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /Swagger.Net/Swagger/XmlComments/ApplyXmlTypeComments.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Xml.XPath; 3 | 4 | namespace Swagger.Net.XmlComments 5 | { 6 | public class ApplyXmlTypeComments : IModelFilter 7 | { 8 | private const string MemberXPath = "/doc/members/member[@name='{0}']"; 9 | private const string SummaryTag = "summary"; 10 | private const string ExampleTag = "example"; 11 | 12 | private readonly XPathNavigator navigator; 13 | 14 | public ApplyXmlTypeComments(string filePath) 15 | : this(new XPathDocument(filePath)) { } 16 | 17 | public ApplyXmlTypeComments(XPathDocument xmlDoc) 18 | { 19 | lock (xmlDoc) 20 | { 21 | navigator = xmlDoc.CreateNavigator(); 22 | } 23 | } 24 | 25 | public void Apply(Schema model, ModelFilterContext context) 26 | { 27 | var commentId = context.SystemType.GetCommentId(); 28 | var typeNode = navigator.SelectSingleNode(string.Format(MemberXPath, commentId)); 29 | 30 | if (typeNode != null) 31 | { 32 | var summaryNode = typeNode.SelectSingleNode( SummaryTag ); 33 | if( summaryNode != null ) 34 | model.description = summaryNode.ExtractContent(); 35 | 36 | var exampleNode = typeNode.SelectSingleNode( ExampleTag ); 37 | if( exampleNode != null ) 38 | model.example = exampleNode.ExtractContent(); 39 | } 40 | 41 | if (model.properties != null) 42 | { 43 | ApplyPropertyComments(model, context); 44 | } 45 | } 46 | 47 | private void ApplyPropertyComments(Schema model, ModelFilterContext context) 48 | { 49 | foreach (var entry in model.properties) 50 | { 51 | var jsonProperty = context.JsonObjectContract.Properties[entry.Key]; 52 | if (jsonProperty == null) continue; 53 | 54 | var propertyInfo = jsonProperty.PropertyInfo(); 55 | if (propertyInfo != null) 56 | { 57 | var propCommentId = propertyInfo.GetCommentId(); 58 | ApplyComments(navigator, entry.Value, propCommentId); 59 | } 60 | else 61 | { 62 | var fieldInfo = jsonProperty.FieldInfo(); 63 | if (fieldInfo != null) 64 | { 65 | var propCommentId = fieldInfo.GetCommentId(); 66 | ApplyComments(navigator, entry.Value, propCommentId); 67 | } 68 | } 69 | } 70 | } 71 | 72 | private void ApplyComments(XPathNavigator navigator, Schema propertySchema, string commentId) 73 | { 74 | var propertyNode = navigator.SelectSingleNode(string.Format(MemberXPath, commentId)); 75 | if (propertyNode == null) return; 76 | 77 | var propSummaryNode = propertyNode.SelectSingleNode(SummaryTag); 78 | if (propSummaryNode != null) 79 | { 80 | propertySchema.description = propSummaryNode.ExtractContent(); 81 | } 82 | 83 | var propExampleNode = propertyNode.SelectSingleNode(ExampleTag); 84 | if (propExampleNode != null) 85 | { 86 | propertySchema.example = propExampleNode.ExtractContent(); 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /Swagger.Net/Application/HttpConfigurationExtensions.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net.Http; 6 | using System.Web.Http; 7 | using System.Web.Http.Routing; 8 | 9 | namespace Swagger.Net.Application 10 | { 11 | public static class HttpConfigurationExtensions 12 | { 13 | private static readonly string DefaultRouteTemplate = "swagger/docs/{apiVersion}"; 14 | 15 | public static SwaggerEnabledConfiguration EnableSwagger( 16 | this HttpConfiguration httpConfig, 17 | Action configure = null) 18 | { 19 | return EnableSwagger(httpConfig, DefaultRouteTemplate, configure); 20 | } 21 | 22 | public static SwaggerEnabledConfiguration EnableSwagger( 23 | this HttpConfiguration httpConfig, 24 | string routeTemplate, 25 | Action configure = null) 26 | { 27 | var config = new SwaggerDocsConfig(); 28 | if (configure != null) configure(config); 29 | 30 | httpConfig.Routes.MapHttpRoute( 31 | name: "swagger_docs" + routeTemplate, 32 | routeTemplate: routeTemplate, 33 | defaults: null, 34 | constraints: new { apiVersion = @".+" }, 35 | handler: new SwaggerDocsHandler(config) 36 | ); 37 | 38 | return new SwaggerEnabledConfiguration(httpConfig, config, routeTemplate); 39 | } 40 | 41 | internal static JsonSerializerSettings SerializerSettingsOrDefault(this HttpConfiguration httpConfig) 42 | { 43 | var formatter = httpConfig.Formatters.JsonFormatter; 44 | if (formatter != null) 45 | return formatter.SerializerSettings; 46 | 47 | return new JsonSerializerSettings(); 48 | } 49 | } 50 | 51 | public class SwaggerEnabledConfiguration 52 | { 53 | private static readonly string DefaultRouteTemplate = "swagger/ui/{*assetPath}"; 54 | 55 | private readonly HttpConfiguration _httpConfig; 56 | private readonly SwaggerDocsConfig _config; 57 | private readonly string _route; 58 | 59 | public SwaggerEnabledConfiguration(HttpConfiguration httpConfig, SwaggerDocsConfig config, string route) 60 | { 61 | _httpConfig = httpConfig; 62 | _config = config; 63 | _route = route; 64 | } 65 | 66 | public void EnableSwaggerUi(Action configure = null) 67 | { 68 | EnableSwaggerUi(DefaultRouteTemplate, configure); 69 | } 70 | 71 | public void EnableSwaggerUi(string routeTemplate, Action configure = null) 72 | { 73 | var config = new SwaggerUiConfig(_config.DiscoveryPaths(_route), _config.GetRootUrl); 74 | configure?.Invoke(config); 75 | 76 | _httpConfig.Routes.MapHttpRoute( 77 | name: "swagger_ui" + routeTemplate, 78 | routeTemplate: routeTemplate, 79 | defaults: null, 80 | constraints: new { assetPath = @".+" }, 81 | handler: new SwaggerUiHandler(config) 82 | ); 83 | 84 | if (routeTemplate == DefaultRouteTemplate) 85 | { 86 | _httpConfig.Routes.MapHttpRoute( 87 | name: "swagger_ui_shortcut", 88 | routeTemplate: "swagger", 89 | defaults: null, 90 | constraints: new { uriResolution = new HttpRouteDirectionConstraint(HttpRouteDirection.UriResolution) }, 91 | handler: new RedirectHandler(_config.GetRootUrl, "swagger/ui/index")); 92 | } 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /Swagger.Net.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2024 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Swagger.Net", "Swagger.Net\Swagger.Net.csproj", "{D50A26FF-02C6-4C16-BA94-4CD7D43EFBB3}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{6A2C69FA-6824-44F3-A1A5-1313050FA567}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C63363AF-2303-4489-8287-95D94C414663}" 11 | ProjectSection(SolutionItems) = preProject 12 | README.md = README.md 13 | EndProjectSection 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Swagger.Net.Tests", "Tests\Swagger.Net.Tests\Swagger.Net.Tests.csproj", "{2F5D233D-F779-473E-9EEC-ACC58AC266ED}" 16 | EndProject 17 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Swagger.Net.Dummy.WebHost", "Tests\Swagger.Net.Dummy.WebHost\Swagger.Net.Dummy.WebHost.csproj", "{E51BDA79-1942-490E-A1D4-E37235736D8A}" 18 | EndProject 19 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Swagger.Net.Dummy.SelfHost", "Tests\Swagger.Net.Dummy.SelfHost\Swagger.Net.Dummy.SelfHost.csproj", "{2D1F2F73-5828-413C-9FD0-6F81A964C82D}" 20 | EndProject 21 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Swagger.Net.Dummy.Core", "Tests\Swagger.Net.Dummy.Core\Swagger.Net.Dummy.Core.csproj", "{2DFE3931-747F-48C6-8530-B74710988095}" 22 | EndProject 23 | Global 24 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 25 | Debug|Any CPU = Debug|Any CPU 26 | Release|Any CPU = Release|Any CPU 27 | EndGlobalSection 28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 29 | {D50A26FF-02C6-4C16-BA94-4CD7D43EFBB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {D50A26FF-02C6-4C16-BA94-4CD7D43EFBB3}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {D50A26FF-02C6-4C16-BA94-4CD7D43EFBB3}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {D50A26FF-02C6-4C16-BA94-4CD7D43EFBB3}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {2F5D233D-F779-473E-9EEC-ACC58AC266ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {2F5D233D-F779-473E-9EEC-ACC58AC266ED}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {2F5D233D-F779-473E-9EEC-ACC58AC266ED}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {2F5D233D-F779-473E-9EEC-ACC58AC266ED}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {E51BDA79-1942-490E-A1D4-E37235736D8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {E51BDA79-1942-490E-A1D4-E37235736D8A}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {E51BDA79-1942-490E-A1D4-E37235736D8A}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {E51BDA79-1942-490E-A1D4-E37235736D8A}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {2D1F2F73-5828-413C-9FD0-6F81A964C82D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {2D1F2F73-5828-413C-9FD0-6F81A964C82D}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {2D1F2F73-5828-413C-9FD0-6F81A964C82D}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {2D1F2F73-5828-413C-9FD0-6F81A964C82D}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {2DFE3931-747F-48C6-8530-B74710988095}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {2DFE3931-747F-48C6-8530-B74710988095}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {2DFE3931-747F-48C6-8530-B74710988095}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {2DFE3931-747F-48C6-8530-B74710988095}.Release|Any CPU.Build.0 = Release|Any CPU 49 | EndGlobalSection 50 | GlobalSection(SolutionProperties) = preSolution 51 | HideSolutionNode = FALSE 52 | EndGlobalSection 53 | GlobalSection(NestedProjects) = preSolution 54 | {2F5D233D-F779-473E-9EEC-ACC58AC266ED} = {6A2C69FA-6824-44F3-A1A5-1313050FA567} 55 | {E51BDA79-1942-490E-A1D4-E37235736D8A} = {6A2C69FA-6824-44F3-A1A5-1313050FA567} 56 | {2D1F2F73-5828-413C-9FD0-6F81A964C82D} = {6A2C69FA-6824-44F3-A1A5-1313050FA567} 57 | {2DFE3931-747F-48C6-8530-B74710988095} = {6A2C69FA-6824-44F3-A1A5-1313050FA567} 58 | EndGlobalSection 59 | GlobalSection(ExtensibilityGlobals) = postSolution 60 | SolutionGuid = {9409349D-75B0-4517-81D8-8F1DD9C78F00} 61 | EndGlobalSection 62 | EndGlobal 63 | -------------------------------------------------------------------------------- /Swagger.Net/SwaggerUi/EmbeddedAssetProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.Serialization.Json; 6 | 7 | namespace Swagger.Net.SwaggerUi 8 | { 9 | public class EmbeddedAssetProvider : IAssetProvider 10 | { 11 | private readonly IDictionary _pathToAssetMap; 12 | private readonly IDictionary _templateParams; 13 | 14 | public EmbeddedAssetProvider( 15 | IDictionary pathToAssetMap, 16 | IDictionary templateParams) 17 | { 18 | _pathToAssetMap = pathToAssetMap; 19 | _templateParams = templateParams; 20 | } 21 | 22 | public Asset GetAsset(string rootUrl, string path) 23 | { 24 | if (path == "index/ext") 25 | { 26 | return GetAssetMap(); 27 | } 28 | else 29 | { 30 | if (!_pathToAssetMap.ContainsKey(path)) 31 | throw new AssetNotFound(String.Format("Mapping not found - {0}", path)); 32 | 33 | var resourceDescriptor = _pathToAssetMap[path]; 34 | return new Asset( 35 | GetEmbeddedResourceStreamFor(resourceDescriptor, rootUrl), 36 | InferMediaTypeFrom(resourceDescriptor.Name) 37 | ); 38 | } 39 | } 40 | 41 | private Asset GetAssetMap() 42 | { 43 | var paths = _pathToAssetMap.Select(x => x.Key).ToList(); 44 | var ser = new DataContractJsonSerializer(paths.GetType()); 45 | var stream = new MemoryStream(); 46 | ser.WriteObject(stream, paths); 47 | stream.Position = 0; 48 | return new Asset(stream, "application/json"); 49 | } 50 | 51 | public Stream GetEmbeddedResourceStreamFor(EmbeddedAssetDescriptor resourceDescriptor, string rootUrl) 52 | { 53 | var stream = resourceDescriptor.Assembly.GetManifestResourceStream(resourceDescriptor.Name); 54 | if (stream == null) 55 | throw new AssetNotFound(String.Format("Embedded resource not found - {0}", resourceDescriptor.Name)); 56 | 57 | if (resourceDescriptor.IsTemplate) 58 | { 59 | var templateParams = _templateParams 60 | .Union(new[] { new KeyValuePair("%(RootUrl)", rootUrl) }) 61 | .ToDictionary(entry => entry.Key, entry => entry.Value); 62 | 63 | return stream.FindAndReplace(templateParams); 64 | } 65 | 66 | return stream; 67 | } 68 | 69 | public static string InferMediaTypeFrom(string path) 70 | { 71 | var extension = path.Split('.').Last(); 72 | 73 | switch (extension) 74 | { 75 | case "css": 76 | return "text/css"; 77 | case "js": 78 | return "text/javascript"; 79 | case "gif": 80 | return "image/gif"; 81 | case "png": 82 | return "image/png"; 83 | case "eot": 84 | return "application/vnd.ms-fontobject"; 85 | case "woff": 86 | return "application/font-woff"; 87 | case "woff2": 88 | return "application/font-woff2"; 89 | case "otf": 90 | return "application/font-sfnt"; // formerly "font/opentype" 91 | case "ttf": 92 | return "application/font-sfnt"; // formerly "font/truetype" 93 | case "svg": 94 | return "image/svg+xml"; 95 | case "json": 96 | return "application/json"; 97 | default: 98 | return "text/html"; 99 | } 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /Swagger.Net/Swagger/FromUriParams/HandleFromUriParams.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Web.Http.Description; 4 | 5 | namespace Swagger.Net.FromUriParams 6 | { 7 | public class HandleFromUriParams : IOperationFilter 8 | { 9 | public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) 10 | { 11 | if (operation.parameters == null) return; 12 | 13 | HandleFromUriArrayParams(operation); 14 | HandleFromUriObjectParams(operation, schemaRegistry, apiDescription); 15 | } 16 | 17 | private static void HandleFromUriArrayParams(Operation operation) 18 | { 19 | var fromUriArrayParams = operation.parameters 20 | .Where(param => param.@in == "query" && param.type == "array") 21 | .ToArray(); 22 | 23 | foreach (var param in fromUriArrayParams) 24 | { 25 | param.collectionFormat = "multi"; 26 | } 27 | } 28 | 29 | private void HandleFromUriObjectParams(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) 30 | { 31 | var fromUriObjectParams = operation.parameters 32 | .Where(param => param.@in == "query" && param.type == null) 33 | .ToArray(); 34 | 35 | foreach (var objectParam in fromUriObjectParams) 36 | { 37 | var type = apiDescription.ParameterDescriptions 38 | .Single(paramDesc => paramDesc.Name == objectParam.name) 39 | .ParameterDescriptor.ParameterType; 40 | 41 | var refSchema = schemaRegistry.GetOrRegister(type); 42 | var schema = schemaRegistry.Definitions[refSchema.@ref.Replace("#/definitions/", "")]; 43 | 44 | var qualifier = string.IsNullOrEmpty(objectParam.name) ? "" : (objectParam.name + "."); 45 | ExtractAndAddQueryParams(schema, qualifier, objectParam.required, schemaRegistry, operation.parameters); 46 | operation.parameters.Remove(objectParam); 47 | } 48 | } 49 | 50 | public void ExtractAndAddQueryParams( 51 | Schema sourceSchema, 52 | string sourceQualifier, 53 | bool? sourceRequired, 54 | SchemaRegistry schemaRegistry, 55 | IList operationParams) 56 | { 57 | if (sourceSchema.properties != null) 58 | { 59 | foreach (var entry in sourceSchema.properties) 60 | { 61 | var propertySchema = entry.Value; 62 | if (propertySchema.readOnly == true) continue; 63 | 64 | var required = (sourceRequired == true) 65 | && sourceSchema.required != null && sourceSchema.required.Contains(entry.Key); 66 | 67 | if (propertySchema.@ref != null) 68 | { 69 | var schema = schemaRegistry.Definitions[propertySchema.@ref.Replace("#/definitions/", "")]; 70 | ExtractAndAddQueryParams( 71 | schema, 72 | sourceQualifier + entry.Key.ToCamelCase() + ".", 73 | required, 74 | schemaRegistry, 75 | operationParams); 76 | } 77 | else 78 | { 79 | var param = new Parameter 80 | { 81 | name = sourceQualifier + entry.Key.ToCamelCase(), 82 | @in = "query", 83 | required = required, 84 | description = entry.Value.description 85 | }; 86 | param.PopulateFrom(entry.Value); 87 | if (param.type == "array") 88 | param.collectionFormat = "multi"; 89 | operationParams.Add(param); 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /Tests/Swagger.Net.Dummy.SelfHost/Swagger.Net.Dummy.SelfHost.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {2D1F2F73-5828-413C-9FD0-6F81A964C82D} 8 | Exe 9 | Properties 10 | Swagger.Net.Dummy.SelfHost 11 | Swagger.Net.Dummy.SelfHost 12 | v4.5 13 | 512 14 | ..\ 15 | true 16 | 17 | 18 | AnyCPU 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | ..\..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll 39 | 40 | 41 | 42 | 43 | 44 | 45 | ..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.4\lib\net45\System.Net.Http.Formatting.dll 46 | 47 | 48 | ..\..\packages\Microsoft.AspNet.WebApi.Core.5.2.4\lib\net45\System.Web.Http.dll 49 | 50 | 51 | ..\..\packages\Microsoft.AspNet.WebApi.SelfHost.5.2.4\lib\net45\System.Web.Http.SelfHost.dll 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | {d50a26ff-02c6-4c16-ba94-4cd7d43efbb3} 62 | Swagger.Net 63 | 64 | 65 | {2dfe3931-747f-48c6-8530-b74710988095} 66 | Swagger.Net.Dummy.Core 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 81 | -------------------------------------------------------------------------------- /Tests/Swagger.Net.Tests/HttpMessageHandlerTestBase.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Net.Http; 5 | using System.Net.Http.Headers; 6 | using System.Reflection; 7 | using System.Threading; 8 | using System.Web.Http; 9 | using System.Web.Http.Hosting; 10 | using System.Web.Http.Routing; 11 | 12 | namespace Swagger.Net.Tests 13 | { 14 | [TestFixture] 15 | public abstract class HttpMessageHandlerTestBase 16 | where THandler : HttpMessageHandler 17 | { 18 | public struct TEMP_URI 19 | { 20 | public const string DOCS = "http://tempuri.org/swagger/docs/v1"; 21 | public const string INDEX = "http://tempuri.org/swagger/ui/index"; 22 | } 23 | 24 | private string _routeTemplate; 25 | 26 | protected HttpMessageHandlerTestBase(string routeTemplate) 27 | { 28 | _routeTemplate = routeTemplate; 29 | } 30 | 31 | protected HttpConfiguration Configuration { get; set; } 32 | 33 | protected THandler Handler { get; set; } 34 | 35 | [SetUp] 36 | public void BaseSetUp() 37 | { 38 | Configuration = new HttpConfiguration(); 39 | } 40 | 41 | protected void SetUpDefaultRoutesFor(IEnumerable controllerTypes) 42 | { 43 | foreach (var type in controllerTypes) 44 | { 45 | var controllerName = type.Name.ToLower().Replace("controller", String.Empty); 46 | var route = new HttpRoute( 47 | String.Format("{0}/{{id}}", controllerName), 48 | new HttpRouteValueDictionary(new { controller = controllerName, id = RouteParameter.Optional })); 49 | Configuration.Routes.Add(controllerName, route); 50 | } 51 | } 52 | 53 | protected void SetUpDefaultRouteFor() 54 | where TController : ApiController 55 | { 56 | SetUpDefaultRoutesFor(new[] { typeof(TController) }); 57 | } 58 | 59 | protected void SetUpCustomRouteFor(string routeTemplate) 60 | where TController : ApiController 61 | { 62 | var controllerName = typeof(TController).Name.ToLower().Replace("controller", String.Empty); 63 | var route = new HttpRoute( 64 | routeTemplate, 65 | new HttpRouteValueDictionary(new { controller = controllerName, id = RouteParameter.Optional })); 66 | Configuration.Routes.Add(controllerName, route); 67 | } 68 | 69 | protected void SetUpAttributeRoutesFrom(Assembly assembly) 70 | { 71 | // assembly isn't used but requiring it ensures that it's loaded and, therefore, scanned for attribute routes 72 | Configuration.MapHttpAttributeRoutes(); 73 | Configuration.EnsureInitialized(); 74 | } 75 | 76 | protected HttpResponseMessage Get(string uri, DateTimeOffset? IfModifiedSince = null) 77 | { 78 | if (Handler == null) 79 | throw new InvalidOperationException("Handler must be set by fixture subclass"); 80 | 81 | var request = new HttpRequestMessage(HttpMethod.Get, uri); 82 | request.Properties[HttpPropertyKeys.HttpConfigurationKey] = Configuration; 83 | request.Headers.IfModifiedSince = IfModifiedSince; 84 | 85 | var route = new HttpRoute(_routeTemplate); 86 | var routeData = route.GetRouteData("/", request) ?? new HttpRouteData(route); 87 | 88 | request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData; 89 | 90 | return new HttpMessageInvoker(Handler) 91 | .SendAsync(request, new CancellationToken(false)) 92 | .Result; 93 | } 94 | 95 | protected TContent GetContent(string uri) 96 | { 97 | var responseMessage = Get(uri); 98 | return responseMessage.Content.ReadAsAsync().Result; 99 | } 100 | 101 | protected HttpResponseHeaders GetHeaders(string uri) 102 | { 103 | var responseMessage = Get(uri); 104 | return responseMessage.Headers; 105 | } 106 | 107 | protected string GetContentAsString(string uri) 108 | { 109 | try 110 | { 111 | var responseMessage = Get(uri); 112 | return responseMessage.Content.ReadAsStringAsync().Result; 113 | } 114 | catch (Exception e) 115 | { 116 | return e.ToString(); 117 | } 118 | } 119 | } 120 | } --------------------------------------------------------------------------------