diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AspNetCoreServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AspNetCoreServerCodegen.java
index 5f87c4f3dc8..287f0a64c7e 100644
--- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AspNetCoreServerCodegen.java
+++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AspNetCoreServerCodegen.java
@@ -120,6 +120,9 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
supportingFiles.add(new SupportingFile("Properties" + File.separator + "launchSettings.json", packageFolder + File.separator + "Properties", "launchSettings.json"));
+ supportingFiles.add(new SupportingFile("Filters" + File.separator + "BasePathDocumentFilter.mustache", packageFolder + File.separator + "Filters", "BasePathDocumentFilter.cs"));
+ supportingFiles.add(new SupportingFile("Filters" + File.separator + "PathParameterValidationFilter.mustache", packageFolder + File.separator + "Filters", "PathParameterValidationFilter.cs"));
+
supportingFiles.add(new SupportingFile("wwwroot" + File.separator + "README.md", packageFolder + File.separator + "wwwroot", "README.md"));
supportingFiles.add(new SupportingFile("wwwroot" + File.separator + "index.html", packageFolder + File.separator + "wwwroot", "index.html"));
supportingFiles.add(new SupportingFile("wwwroot" + File.separator + "web.config", packageFolder + File.separator + "wwwroot", "web.config"));
diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/Filters/BasePathDocumentFilter.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/Filters/BasePathDocumentFilter.mustache
new file mode 100644
index 00000000000..a6b2aa9426d
--- /dev/null
+++ b/modules/swagger-codegen/src/main/resources/aspnetcore/Filters/BasePathDocumentFilter.mustache
@@ -0,0 +1,50 @@
+using System.Linq;
+using System.Text.RegularExpressions;
+using Swashbuckle.AspNetCore.Swagger;
+using Swashbuckle.AspNetCore.SwaggerGen;
+
+namespace {{packageName}}.Filters
+{
+ ///
+ /// BasePath Document Filter sets BasePath property of Swagger and removes it from the individual URL paths
+ ///
+ public class BasePathDocumentFilter : IDocumentFilter
+ {
+ ///
+ /// Constructor
+ ///
+ /// BasePath to remove from Operations
+ public BasePathDocumentFilter(string basePath)
+ {
+ BasePath = basePath;
+ }
+
+ ///
+ /// Gets the BasePath of the Swagger Doc
+ ///
+ /// The BasePath of the Swagger Doc
+ public string BasePath { get; }
+
+ ///
+ /// Apply the filter
+ ///
+ /// SwaggerDocument
+ /// FilterContext
+ public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
+ {
+ swaggerDoc.BasePath = this.BasePath;
+
+ var pathsToModify = swaggerDoc.Paths.Where(p => p.Key.StartsWith(this.BasePath)).ToList();
+
+ foreach (var path in pathsToModify)
+ {
+ if (path.Key.StartsWith(this.BasePath))
+ {
+ string newKey = Regex.Replace(path.Key, $"^{this.BasePath}", string.Empty);
+ swaggerDoc.Paths.Remove(path.Key);
+ swaggerDoc.Paths.Add(newKey, path.Value);
+ }
+ }
+ }
+ }
+}
diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/Filters/PathParameterValidationFilter.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/Filters/PathParameterValidationFilter.mustache
new file mode 100644
index 00000000000..da388c8d4d1
--- /dev/null
+++ b/modules/swagger-codegen/src/main/resources/aspnetcore/Filters/PathParameterValidationFilter.mustache
@@ -0,0 +1,97 @@
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Swashbuckle.AspNetCore.Swagger;
+using Swashbuckle.AspNetCore.SwaggerGen;
+
+namespace {{packageName}}.Filters
+{
+ ///
+ /// Path Parameter Validation Filter
+ ///
+ public class PathParameterValidationFilter : IOperationFilter
+ {
+ ///
+ /// Constructor
+ ///
+ /// Operation
+ /// OperationFilterContext
+ public void Apply(Operation operation, OperationFilterContext context)
+ {
+ var pars = context.ApiDescription.ParameterDescriptions;
+
+ foreach (var par in pars)
+ {
+ var swaggerParam = operation.Parameters.SingleOrDefault(p => p.Name == par.Name);
+
+ var attributes = ((ControllerParameterDescriptor)par.ParameterDescriptor).ParameterInfo.CustomAttributes;
+
+ if (attributes != null && attributes.Count() > 0 && swaggerParam != null)
+ {
+ // Required - [Required]
+ var requiredAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(RequiredAttribute));
+ if (requiredAttr != null)
+ {
+ swaggerParam.Required = true;
+ }
+
+ // Regex Pattern [RegularExpression]
+ var regexAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(RegularExpressionAttribute));
+ if (regexAttr != null)
+ {
+ string regex = (string)regexAttr.ConstructorArguments[0].Value;
+ if (swaggerParam is NonBodyParameter)
+ {
+ ((NonBodyParameter)swaggerParam).Pattern = regex;
+ }
+ }
+
+ // String Length [StringLength]
+ int? minLenght = null, maxLength = null;
+ var stringLengthAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(StringLengthAttribute));
+ if (stringLengthAttr != null)
+ {
+ if (stringLengthAttr.NamedArguments.Count == 1)
+ {
+ minLenght = (int)stringLengthAttr.NamedArguments.Single(p => p.MemberName == "MinimumLength").TypedValue.Value;
+ }
+ maxLength = (int)stringLengthAttr.ConstructorArguments[0].Value;
+ }
+
+ var minLengthAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(MinLengthAttribute));
+ if (minLengthAttr != null)
+ {
+ minLenght = (int)minLengthAttr.ConstructorArguments[0].Value;
+ }
+
+ var maxLengthAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(MaxLengthAttribute));
+ if (maxLengthAttr != null)
+ {
+ maxLength = (int)maxLengthAttr.ConstructorArguments[0].Value;
+ }
+
+ if (swaggerParam is NonBodyParameter)
+ {
+ ((NonBodyParameter)swaggerParam).MinLength = minLenght;
+ ((NonBodyParameter)swaggerParam).MaxLength = maxLength;
+ }
+
+ // Range [Range]
+ var rangeAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(RangeAttribute));
+ if (rangeAttr != null)
+ {
+ int rangeMin = (int)rangeAttr.ConstructorArguments[0].Value;
+ int rangeMax = (int)rangeAttr.ConstructorArguments[1].Value;
+
+ if (swaggerParam is NonBodyParameter)
+ {
+ ((NonBodyParameter)swaggerParam).Minimum = rangeMin;
+ ((NonBodyParameter)swaggerParam).Maximum = rangeMax;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/Startup.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/Startup.mustache
index 47009226f2d..fd9178dc536 100644
--- a/modules/swagger-codegen/src/main/resources/aspnetcore/Startup.mustache
+++ b/modules/swagger-codegen/src/main/resources/aspnetcore/Startup.mustache
@@ -10,6 +10,7 @@ using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
+using {{packageName}}.Filters;
namespace {{packageName}}
{
@@ -62,6 +63,12 @@ namespace {{packageName}}
c.CustomSchemaIds(type => type.FriendlyId(true));
c.DescribeAllEnumsAsStrings();
c.IncludeXmlComments($"{AppContext.BaseDirectory}{Path.DirectorySeparatorChar}{_hostingEnv.ApplicationName}.xml");
+ {{#basePathWithoutHost}}
+ // Sets the basePath property in the Swagger document generated
+ c.DocumentFilter("{{{basePathWithoutHost}}}");
+ {{/basePathWithoutHost}}
+ // Do validation of path parameters w. DataAnnotation attributes
+ c.OperationFilter();
});
}
diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/controller.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/controller.mustache
index e89ca80cc0d..05db73f1a0c 100644
--- a/modules/swagger-codegen/src/main/resources/aspnetcore/controller.mustache
+++ b/modules/swagger-codegen/src/main/resources/aspnetcore/controller.mustache
@@ -11,6 +11,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Swashbuckle.AspNetCore.SwaggerGen;
using Newtonsoft.Json;
+using System.ComponentModel.DataAnnotations;
using {{packageName}}.Attributes;
using {{packageName}}.Models;
diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/formParam.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/formParam.mustache
index 1e743e1e4c7..2d42dc2916b 100644
--- a/modules/swagger-codegen/src/main/resources/aspnetcore/formParam.mustache
+++ b/modules/swagger-codegen/src/main/resources/aspnetcore/formParam.mustache
@@ -1 +1 @@
-{{#isFormParam}}[FromForm]{{&dataType}} {{paramName}}{{/isFormParam}}
\ No newline at end of file
+{{#isFormParam}}[FromForm]{{#required}}[Required()]{{/required}}{{#pattern}}[RegularExpression("{{{pattern}}}")]{{/pattern}}{{#minLength}}{{#maxLength}}[StringLength({{maxLength}}, MinimumLength={{minLength}}){{/maxLength}}{{/minLength}}{{#minLength}}{{^maxLength}} [MinLength({{minLength}})]{{/maxLength}}{{/minLength}}{{^minLength}}{{#maxLength}} [MaxLength({{maxLength}})]{{/maxLength}}{{/minLength}}{{#minimum}}{{#maximum}}[Range({{minimum}}, {{maximum}})]{{/maximum}}{{/minimum}}{{&dataType}} {{paramName}}{{/isFormParam}}
\ No newline at end of file
diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/headerParam.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/headerParam.mustache
index e61cadb1131..45a5be9d7b5 100644
--- a/modules/swagger-codegen/src/main/resources/aspnetcore/headerParam.mustache
+++ b/modules/swagger-codegen/src/main/resources/aspnetcore/headerParam.mustache
@@ -1 +1 @@
-{{#isHeaderParam}}[FromHeader]{{&dataType}} {{paramName}}{{/isHeaderParam}}
\ No newline at end of file
+{{#isHeaderParam}}[FromHeader]{{#required}}[Required()]{{/required}}{{#pattern}}[RegularExpression("{{{pattern}}}")]{{/pattern}}{{#minLength}}{{#maxLength}}[StringLength({{maxLength}}, MinimumLength={{minLength}}){{/maxLength}}{{/minLength}}{{#minLength}}{{^maxLength}} [MinLength({{minLength}})]{{/maxLength}}{{/minLength}}{{^minLength}}{{#maxLength}} [MaxLength({{maxLength}})]{{/maxLength}}{{/minLength}}{{#minimum}}{{#maximum}}[Range({{minimum}}, {{maximum}})]{{/maximum}}{{/minimum}}{{&dataType}} {{paramName}}{{/isHeaderParam}}
\ No newline at end of file
diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/pathParam.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/pathParam.mustache
index 5aa27eb4cb3..70303432d48 100644
--- a/modules/swagger-codegen/src/main/resources/aspnetcore/pathParam.mustache
+++ b/modules/swagger-codegen/src/main/resources/aspnetcore/pathParam.mustache
@@ -1 +1 @@
-{{#isPathParam}}[FromRoute]{{&dataType}} {{paramName}}{{/isPathParam}}
\ No newline at end of file
+{{#isPathParam}}[FromRoute]{{#required}}[Required]{{/required}}{{#pattern}}[RegularExpression("{{{pattern}}}")]{{/pattern}}{{#minLength}}{{#maxLength}}[StringLength({{maxLength}}, MinimumLength={{minLength}}){{/maxLength}}{{/minLength}}{{#minLength}}{{^maxLength}} [MinLength({{minLength}})]{{/maxLength}}{{/minLength}}{{^minLength}}{{#maxLength}} [MaxLength({{maxLength}})]{{/maxLength}}{{/minLength}}{{#minimum}}{{#maximum}}[Range({{minimum}}, {{maximum}})]{{/maximum}}{{/minimum}}{{&dataType}} {{paramName}}{{/isPathParam}}
\ No newline at end of file
diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/queryParam.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/queryParam.mustache
index 42ce87a2b7f..e9fa09b005d 100644
--- a/modules/swagger-codegen/src/main/resources/aspnetcore/queryParam.mustache
+++ b/modules/swagger-codegen/src/main/resources/aspnetcore/queryParam.mustache
@@ -1 +1 @@
-{{#isQueryParam}}[FromQuery]{{&dataType}} {{paramName}}{{/isQueryParam}}
\ No newline at end of file
+{{#isQueryParam}}[FromQuery]{{#required}}[Required()]{{/required}}{{#pattern}}[RegularExpression("{{{pattern}}}")]{{/pattern}}{{#minLength}}{{#maxLength}}[StringLength({{maxLength}}, MinimumLength={{minLength}}){{/maxLength}}{{/minLength}}{{#minLength}}{{^maxLength}} [MinLength({{minLength}})]{{/maxLength}}{{/minLength}}{{^minLength}}{{#maxLength}} [MaxLength({{maxLength}})]{{/maxLength}}{{/minLength}}{{#minimum}}{{#maximum}}[Range({{minimum}}, {{maximum}})]{{/maximum}}{{/minimum}}{{&dataType}} {{paramName}}{{/isQueryParam}}
\ No newline at end of file
diff --git a/samples/server/petstore/aspnetcore/src/IO.Swagger/Controllers/PetApi.cs b/samples/server/petstore/aspnetcore/src/IO.Swagger/Controllers/PetApi.cs
index 60dfd3bbb87..af0b192f661 100644
--- a/samples/server/petstore/aspnetcore/src/IO.Swagger/Controllers/PetApi.cs
+++ b/samples/server/petstore/aspnetcore/src/IO.Swagger/Controllers/PetApi.cs
@@ -20,6 +20,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Swashbuckle.AspNetCore.SwaggerGen;
using Newtonsoft.Json;
+using System.ComponentModel.DataAnnotations;
using IO.Swagger.Attributes;
using IO.Swagger.Models;
@@ -60,7 +61,7 @@ namespace IO.Swagger.Controllers
[Route("/v2/pet/{petId}")]
[ValidateModelState]
[SwaggerOperation("DeletePet")]
- public virtual IActionResult DeletePet([FromRoute]long? petId, [FromHeader]string apiKey)
+ public virtual IActionResult DeletePet([FromRoute][Required]long? petId, [FromHeader]string apiKey)
{
//TODO: Uncomment the next line to return response 400 or use other options such as return this.NotFound(), return this.BadRequest(..), ...
// return StatusCode(400);
@@ -82,7 +83,7 @@ namespace IO.Swagger.Controllers
[SwaggerOperation("FindPetsByStatus")]
[SwaggerResponse(200, typeof(List), "successful operation")]
[SwaggerResponse(400, typeof(List), "Invalid status value")]
- public virtual IActionResult FindPetsByStatus([FromQuery]List status)
+ public virtual IActionResult FindPetsByStatus([FromQuery][Required()]List status)
{
//TODO: Uncomment the next line to return response 200 or use other options such as return this.NotFound(), return this.BadRequest(..), ...
// return StatusCode(200, default(List));
@@ -112,7 +113,7 @@ namespace IO.Swagger.Controllers
[SwaggerOperation("FindPetsByTags")]
[SwaggerResponse(200, typeof(List), "successful operation")]
[SwaggerResponse(400, typeof(List), "Invalid tag value")]
- public virtual IActionResult FindPetsByTags([FromQuery]List tags)
+ public virtual IActionResult FindPetsByTags([FromQuery][Required()]List tags)
{
//TODO: Uncomment the next line to return response 200 or use other options such as return this.NotFound(), return this.BadRequest(..), ...
// return StatusCode(200, default(List));
@@ -144,7 +145,7 @@ namespace IO.Swagger.Controllers
[SwaggerResponse(200, typeof(Pet), "successful operation")]
[SwaggerResponse(400, typeof(Pet), "Invalid ID supplied")]
[SwaggerResponse(404, typeof(Pet), "Pet not found")]
- public virtual IActionResult GetPetById([FromRoute]long? petId)
+ public virtual IActionResult GetPetById([FromRoute][Required]long? petId)
{
//TODO: Uncomment the next line to return response 200 or use other options such as return this.NotFound(), return this.BadRequest(..), ...
// return StatusCode(200, default(Pet));
@@ -203,7 +204,7 @@ namespace IO.Swagger.Controllers
[Route("/v2/pet/{petId}")]
[ValidateModelState]
[SwaggerOperation("UpdatePetWithForm")]
- public virtual IActionResult UpdatePetWithForm([FromRoute]long? petId, [FromForm]string name, [FromForm]string status)
+ public virtual IActionResult UpdatePetWithForm([FromRoute][Required]long? petId, [FromForm]string name, [FromForm]string status)
{
//TODO: Uncomment the next line to return response 405 or use other options such as return this.NotFound(), return this.BadRequest(..), ...
// return StatusCode(405);
@@ -225,7 +226,7 @@ namespace IO.Swagger.Controllers
[ValidateModelState]
[SwaggerOperation("UploadFile")]
[SwaggerResponse(200, typeof(ApiResponse), "successful operation")]
- public virtual IActionResult UploadFile([FromRoute]long? petId, [FromForm]string additionalMetadata, [FromForm]System.IO.Stream file)
+ public virtual IActionResult UploadFile([FromRoute][Required]long? petId, [FromForm]string additionalMetadata, [FromForm]System.IO.Stream file)
{
//TODO: Uncomment the next line to return response 200 or use other options such as return this.NotFound(), return this.BadRequest(..), ...
// return StatusCode(200, default(ApiResponse));
diff --git a/samples/server/petstore/aspnetcore/src/IO.Swagger/Controllers/StoreApi.cs b/samples/server/petstore/aspnetcore/src/IO.Swagger/Controllers/StoreApi.cs
index edd7cfae6bc..e7dd7610a77 100644
--- a/samples/server/petstore/aspnetcore/src/IO.Swagger/Controllers/StoreApi.cs
+++ b/samples/server/petstore/aspnetcore/src/IO.Swagger/Controllers/StoreApi.cs
@@ -20,6 +20,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Swashbuckle.AspNetCore.SwaggerGen;
using Newtonsoft.Json;
+using System.ComponentModel.DataAnnotations;
using IO.Swagger.Attributes;
using IO.Swagger.Models;
@@ -41,7 +42,7 @@ namespace IO.Swagger.Controllers
[Route("/v2/store/order/{orderId}")]
[ValidateModelState]
[SwaggerOperation("DeleteOrder")]
- public virtual IActionResult DeleteOrder([FromRoute]string orderId)
+ public virtual IActionResult DeleteOrder([FromRoute][Required]string orderId)
{
//TODO: Uncomment the next line to return response 400 or use other options such as return this.NotFound(), return this.BadRequest(..), ...
// return StatusCode(400);
@@ -92,7 +93,7 @@ namespace IO.Swagger.Controllers
[SwaggerResponse(200, typeof(Order), "successful operation")]
[SwaggerResponse(400, typeof(Order), "Invalid ID supplied")]
[SwaggerResponse(404, typeof(Order), "Order not found")]
- public virtual IActionResult GetOrderById([FromRoute]long? orderId)
+ public virtual IActionResult GetOrderById([FromRoute][Required][Range(1, 5)]long? orderId)
{
//TODO: Uncomment the next line to return response 200 or use other options such as return this.NotFound(), return this.BadRequest(..), ...
// return StatusCode(200, default(Order));
diff --git a/samples/server/petstore/aspnetcore/src/IO.Swagger/Controllers/UserApi.cs b/samples/server/petstore/aspnetcore/src/IO.Swagger/Controllers/UserApi.cs
index 85d4e840cf6..ab82c41a1df 100644
--- a/samples/server/petstore/aspnetcore/src/IO.Swagger/Controllers/UserApi.cs
+++ b/samples/server/petstore/aspnetcore/src/IO.Swagger/Controllers/UserApi.cs
@@ -20,6 +20,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Swashbuckle.AspNetCore.SwaggerGen;
using Newtonsoft.Json;
+using System.ComponentModel.DataAnnotations;
using IO.Swagger.Attributes;
using IO.Swagger.Models;
@@ -98,7 +99,7 @@ namespace IO.Swagger.Controllers
[Route("/v2/user/{username}")]
[ValidateModelState]
[SwaggerOperation("DeleteUser")]
- public virtual IActionResult DeleteUser([FromRoute]string username)
+ public virtual IActionResult DeleteUser([FromRoute][Required]string username)
{
//TODO: Uncomment the next line to return response 400 or use other options such as return this.NotFound(), return this.BadRequest(..), ...
// return StatusCode(400);
@@ -125,7 +126,7 @@ namespace IO.Swagger.Controllers
[SwaggerResponse(200, typeof(User), "successful operation")]
[SwaggerResponse(400, typeof(User), "Invalid username supplied")]
[SwaggerResponse(404, typeof(User), "User not found")]
- public virtual IActionResult GetUserByName([FromRoute]string username)
+ public virtual IActionResult GetUserByName([FromRoute][Required]string username)
{
//TODO: Uncomment the next line to return response 200 or use other options such as return this.NotFound(), return this.BadRequest(..), ...
// return StatusCode(200, default(User));
@@ -159,7 +160,7 @@ namespace IO.Swagger.Controllers
[SwaggerOperation("LoginUser")]
[SwaggerResponse(200, typeof(string), "successful operation")]
[SwaggerResponse(400, typeof(string), "Invalid username/password supplied")]
- public virtual IActionResult LoginUser([FromQuery]string username, [FromQuery]string password)
+ public virtual IActionResult LoginUser([FromQuery][Required()]string username, [FromQuery][Required()]string password)
{
//TODO: Uncomment the next line to return response 200 or use other options such as return this.NotFound(), return this.BadRequest(..), ...
// return StatusCode(200, default(string));
@@ -206,7 +207,7 @@ namespace IO.Swagger.Controllers
[Route("/v2/user/{username}")]
[ValidateModelState]
[SwaggerOperation("UpdateUser")]
- public virtual IActionResult UpdateUser([FromRoute]string username, [FromBody]User body)
+ public virtual IActionResult UpdateUser([FromRoute][Required]string username, [FromBody]User body)
{
//TODO: Uncomment the next line to return response 400 or use other options such as return this.NotFound(), return this.BadRequest(..), ...
// return StatusCode(400);
diff --git a/samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/BasePathDocumentFilter.cs b/samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/BasePathDocumentFilter.cs
new file mode 100644
index 00000000000..dc3592f50e5
--- /dev/null
+++ b/samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/BasePathDocumentFilter.cs
@@ -0,0 +1,50 @@
+using System.Linq;
+using System.Text.RegularExpressions;
+using Swashbuckle.AspNetCore.Swagger;
+using Swashbuckle.AspNetCore.SwaggerGen;
+
+namespace IO.Swagger.Filters
+{
+ ///
+ /// BasePath Document Filter sets BasePath property of Swagger and removes it from the individual URL paths
+ ///
+ public class BasePathDocumentFilter : IDocumentFilter
+ {
+ ///
+ /// Constructor
+ ///
+ /// BasePath to remove from Operations
+ public BasePathDocumentFilter(string basePath)
+ {
+ BasePath = basePath;
+ }
+
+ ///
+ /// Gets the BasePath of the Swagger Doc
+ ///
+ /// The BasePath of the Swagger Doc
+ public string BasePath { get; }
+
+ ///
+ /// Apply the filter
+ ///
+ /// SwaggerDocument
+ /// FilterContext
+ public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
+ {
+ swaggerDoc.BasePath = this.BasePath;
+
+ var pathsToModify = swaggerDoc.Paths.Where(p => p.Key.StartsWith(this.BasePath)).ToList();
+
+ foreach (var path in pathsToModify)
+ {
+ if (path.Key.StartsWith(this.BasePath))
+ {
+ string newKey = Regex.Replace(path.Key, $"^{this.BasePath}", string.Empty);
+ swaggerDoc.Paths.Remove(path.Key);
+ swaggerDoc.Paths.Add(newKey, path.Value);
+ }
+ }
+ }
+ }
+}
diff --git a/samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/PathParameterValidationFilter.cs b/samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/PathParameterValidationFilter.cs
new file mode 100644
index 00000000000..798d2cef266
--- /dev/null
+++ b/samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/PathParameterValidationFilter.cs
@@ -0,0 +1,97 @@
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Swashbuckle.AspNetCore.Swagger;
+using Swashbuckle.AspNetCore.SwaggerGen;
+
+namespace IO.Swagger.Filters
+{
+ ///
+ /// Path Parameter Validation Filter
+ ///
+ public class PathParameterValidationFilter : IOperationFilter
+ {
+ ///
+ /// Constructor
+ ///
+ /// Operation
+ /// OperationFilterContext
+ public void Apply(Operation operation, OperationFilterContext context)
+ {
+ var pars = context.ApiDescription.ParameterDescriptions;
+
+ foreach (var par in pars)
+ {
+ var swaggerParam = operation.Parameters.SingleOrDefault(p => p.Name == par.Name);
+
+ var attributes = ((ControllerParameterDescriptor)par.ParameterDescriptor).ParameterInfo.CustomAttributes;
+
+ if (attributes != null && attributes.Count() > 0 && swaggerParam != null)
+ {
+ // Required - [Required]
+ var requiredAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(RequiredAttribute));
+ if (requiredAttr != null)
+ {
+ swaggerParam.Required = true;
+ }
+
+ // Regex Pattern [RegularExpression]
+ var regexAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(RegularExpressionAttribute));
+ if (regexAttr != null)
+ {
+ string regex = (string)regexAttr.ConstructorArguments[0].Value;
+ if (swaggerParam is NonBodyParameter)
+ {
+ ((NonBodyParameter)swaggerParam).Pattern = regex;
+ }
+ }
+
+ // String Length [StringLength]
+ int? minLenght = null, maxLength = null;
+ var stringLengthAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(StringLengthAttribute));
+ if (stringLengthAttr != null)
+ {
+ if (stringLengthAttr.NamedArguments.Count == 1)
+ {
+ minLenght = (int)stringLengthAttr.NamedArguments.Single(p => p.MemberName == "MinimumLength").TypedValue.Value;
+ }
+ maxLength = (int)stringLengthAttr.ConstructorArguments[0].Value;
+ }
+
+ var minLengthAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(MinLengthAttribute));
+ if (minLengthAttr != null)
+ {
+ minLenght = (int)minLengthAttr.ConstructorArguments[0].Value;
+ }
+
+ var maxLengthAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(MaxLengthAttribute));
+ if (maxLengthAttr != null)
+ {
+ maxLength = (int)maxLengthAttr.ConstructorArguments[0].Value;
+ }
+
+ if (swaggerParam is NonBodyParameter)
+ {
+ ((NonBodyParameter)swaggerParam).MinLength = minLenght;
+ ((NonBodyParameter)swaggerParam).MaxLength = maxLength;
+ }
+
+ // Range [Range]
+ var rangeAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(RangeAttribute));
+ if (rangeAttr != null)
+ {
+ int rangeMin = (int)rangeAttr.ConstructorArguments[0].Value;
+ int rangeMax = (int)rangeAttr.ConstructorArguments[1].Value;
+
+ if (swaggerParam is NonBodyParameter)
+ {
+ ((NonBodyParameter)swaggerParam).Minimum = rangeMin;
+ ((NonBodyParameter)swaggerParam).Maximum = rangeMax;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/samples/server/petstore/aspnetcore/src/IO.Swagger/Startup.cs b/samples/server/petstore/aspnetcore/src/IO.Swagger/Startup.cs
index 79024f4e6a1..0b7d570bb03 100644
--- a/samples/server/petstore/aspnetcore/src/IO.Swagger/Startup.cs
+++ b/samples/server/petstore/aspnetcore/src/IO.Swagger/Startup.cs
@@ -19,6 +19,7 @@ using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
+using IO.Swagger.Filters;
namespace IO.Swagger
{
@@ -71,6 +72,10 @@ namespace IO.Swagger
c.CustomSchemaIds(type => type.FriendlyId(true));
c.DescribeAllEnumsAsStrings();
c.IncludeXmlComments($"{AppContext.BaseDirectory}{Path.DirectorySeparatorChar}{_hostingEnv.ApplicationName}.xml");
+ // Sets the basePath property in the Swagger document generated
+ c.DocumentFilter("/v2");
+ // Do validation of path parameters w. DataAnnotation attributes
+ c.OperationFilter();
});
}