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 f1bd73de503..22de7a1e6f3 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
@@ -125,8 +125,8 @@ 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("Filters" + File.separator + "BasePathFilter.mustache", packageFolder + File.separator + "Filters", "BasePathFilter.cs"));
+ supportingFiles.add(new SupportingFile("Filters" + File.separator + "GeneratePathParamsValidationFilter.mustache", packageFolder + File.separator + "Filters", "GeneratePathParamsValidationFilter.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"));
diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/Filters/BasePathDocumentFilter.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/Filters/BasePathFilter.mustache
similarity index 93%
rename from modules/swagger-codegen/src/main/resources/aspnetcore/Filters/BasePathDocumentFilter.mustache
rename to modules/swagger-codegen/src/main/resources/aspnetcore/Filters/BasePathFilter.mustache
index a6b2aa9426d..bc76e3accc6 100644
--- a/modules/swagger-codegen/src/main/resources/aspnetcore/Filters/BasePathDocumentFilter.mustache
+++ b/modules/swagger-codegen/src/main/resources/aspnetcore/Filters/BasePathFilter.mustache
@@ -8,13 +8,13 @@ namespace {{packageName}}.Filters
///
/// BasePath Document Filter sets BasePath property of Swagger and removes it from the individual URL paths
///
- public class BasePathDocumentFilter : IDocumentFilter
+ public class BasePathFilter : IDocumentFilter
{
///
/// Constructor
///
/// BasePath to remove from Operations
- public BasePathDocumentFilter(string basePath)
+ public BasePathFilter(string basePath)
{
BasePath = basePath;
}
diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/Filters/PathParameterValidationFilter.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/Filters/GeneratePathParamsValidationFilter.mustache
similarity index 97%
rename from modules/swagger-codegen/src/main/resources/aspnetcore/Filters/PathParameterValidationFilter.mustache
rename to modules/swagger-codegen/src/main/resources/aspnetcore/Filters/GeneratePathParamsValidationFilter.mustache
index da388c8d4d1..d857a4a0f96 100644
--- a/modules/swagger-codegen/src/main/resources/aspnetcore/Filters/PathParameterValidationFilter.mustache
+++ b/modules/swagger-codegen/src/main/resources/aspnetcore/Filters/GeneratePathParamsValidationFilter.mustache
@@ -7,9 +7,9 @@ using Swashbuckle.AspNetCore.SwaggerGen;
namespace {{packageName}}.Filters
{
///
- /// Path Parameter Validation Filter
+ /// Path Parameter Validation Rules Filter
///
- public class PathParameterValidationFilter : IOperationFilter
+ public class GeneratePathParamsValidationFilter : IOperationFilter
{
///
/// Constructor
diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/Startup.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/Startup.mustache
index 3d95d167ea0..5b95562dcdc 100644
--- a/modules/swagger-codegen/src/main/resources/aspnetcore/Startup.mustache
+++ b/modules/swagger-codegen/src/main/resources/aspnetcore/Startup.mustache
@@ -72,10 +72,12 @@ namespace {{packageName}}
c.IncludeXmlComments($"{AppContext.BaseDirectory}{Path.DirectorySeparatorChar}{_hostingEnv.ApplicationName}.xml");
{{#basePathWithoutHost}}
// Sets the basePath property in the Swagger document generated
- c.DocumentFilter("{{{basePathWithoutHost}}}");
+ c.DocumentFilter("{{{basePathWithoutHost}}}");
{{/basePathWithoutHost}}
- // Do validation of path parameters w. DataAnnotation attributes
- c.OperationFilter();
+
+ // Include DataAnnotation attributes on Controller Action parameters as Swagger validation rules (e.g required, pattern, ..)
+ // Use [ValidateModelState] on Actions to actually validate it in C# as well!
+ c.OperationFilter();
});
}
diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/validateModel.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/validateModel.mustache
index 9f850f71d93..e11aaa5d270 100644
--- a/modules/swagger-codegen/src/main/resources/aspnetcore/validateModel.mustache
+++ b/modules/swagger-codegen/src/main/resources/aspnetcore/validateModel.mustache
@@ -1,5 +1,9 @@
+using System.ComponentModel.DataAnnotations;
+using System.Reflection;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace {{packageName}}.Attributes
{
@@ -14,10 +18,44 @@ namespace {{packageName}}.Attributes
///
public override void OnActionExecuting(ActionExecutingContext context)
{
+ // Per https://blog.markvincze.com/how-to-validate-action-parameters-with-dataannotation-attributes/
+ var descriptor = context.ActionDescriptor as ControllerActionDescriptor;
+ if (descriptor != null)
+ {
+ foreach (var parameter in descriptor.MethodInfo.GetParameters())
+ {
+ object args = null;
+ if (context.ActionArguments.ContainsKey(parameter.Name))
+ {
+ args = context.ActionArguments[parameter.Name];
+ }
+
+ ValidateAttributes(parameter, args, context.ModelState);
+ }
+ }
+
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(context.ModelState);
}
}
+
+ private void ValidateAttributes(ParameterInfo parameter, object args, ModelStateDictionary modelState)
+ {
+ foreach (var attributeData in parameter.CustomAttributes)
+ {
+ var attributeInstance = parameter.GetCustomAttribute(attributeData.AttributeType);
+
+ var validationAttribute = attributeInstance as ValidationAttribute;
+ if (validationAttribute != null)
+ {
+ var isValid = validationAttribute.IsValid(args);
+ if (!isValid)
+ {
+ modelState.AddModelError(parameter.Name, validationAttribute.FormatErrorMessage(parameter.Name));
+ }
+ }
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/samples/server/petstore/aspnetcore/src/IO.Swagger/Attributes/ValidateModelStateAttribute.cs b/samples/server/petstore/aspnetcore/src/IO.Swagger/Attributes/ValidateModelStateAttribute.cs
index 963f934dc52..07cfabe83cf 100644
--- a/samples/server/petstore/aspnetcore/src/IO.Swagger/Attributes/ValidateModelStateAttribute.cs
+++ b/samples/server/petstore/aspnetcore/src/IO.Swagger/Attributes/ValidateModelStateAttribute.cs
@@ -1,5 +1,9 @@
+using System.ComponentModel.DataAnnotations;
+using System.Reflection;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace IO.Swagger.Attributes
{
@@ -14,10 +18,44 @@ namespace IO.Swagger.Attributes
///
public override void OnActionExecuting(ActionExecutingContext context)
{
+ // Per https://blog.markvincze.com/how-to-validate-action-parameters-with-dataannotation-attributes/
+ var descriptor = context.ActionDescriptor as ControllerActionDescriptor;
+ if (descriptor != null)
+ {
+ foreach (var parameter in descriptor.MethodInfo.GetParameters())
+ {
+ object args = null;
+ if (context.ActionArguments.ContainsKey(parameter.Name))
+ {
+ args = context.ActionArguments[parameter.Name];
+ }
+
+ ValidateAttributes(parameter, args, context.ModelState);
+ }
+ }
+
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(context.ModelState);
}
}
+
+ private void ValidateAttributes(ParameterInfo parameter, object args, ModelStateDictionary modelState)
+ {
+ foreach (var attributeData in parameter.CustomAttributes)
+ {
+ var attributeInstance = parameter.GetCustomAttribute(attributeData.AttributeType);
+
+ var validationAttribute = attributeInstance as ValidationAttribute;
+ if (validationAttribute != null)
+ {
+ var isValid = validationAttribute.IsValid(args);
+ if (!isValid)
+ {
+ modelState.AddModelError(parameter.Name, validationAttribute.FormatErrorMessage(parameter.Name));
+ }
+ }
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/BasePathDocumentFilter.cs b/samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/BasePathFilter.cs
similarity index 93%
rename from samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/BasePathDocumentFilter.cs
rename to samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/BasePathFilter.cs
index dc3592f50e5..f3c528eada2 100644
--- a/samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/BasePathDocumentFilter.cs
+++ b/samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/BasePathFilter.cs
@@ -8,13 +8,13 @@ namespace IO.Swagger.Filters
///
/// BasePath Document Filter sets BasePath property of Swagger and removes it from the individual URL paths
///
- public class BasePathDocumentFilter : IDocumentFilter
+ public class BasePathFilter : IDocumentFilter
{
///
/// Constructor
///
/// BasePath to remove from Operations
- public BasePathDocumentFilter(string basePath)
+ public BasePathFilter(string basePath)
{
BasePath = basePath;
}
diff --git a/samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/PathParameterValidationFilter.cs b/samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/GeneratePathParamsValidationFilter.cs
similarity index 97%
rename from samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/PathParameterValidationFilter.cs
rename to samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/GeneratePathParamsValidationFilter.cs
index 798d2cef266..a0e2cb6871d 100644
--- a/samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/PathParameterValidationFilter.cs
+++ b/samples/server/petstore/aspnetcore/src/IO.Swagger/Filters/GeneratePathParamsValidationFilter.cs
@@ -7,9 +7,9 @@ using Swashbuckle.AspNetCore.SwaggerGen;
namespace IO.Swagger.Filters
{
///
- /// Path Parameter Validation Filter
+ /// Path Parameter Validation Rules Filter
///
- public class PathParameterValidationFilter : IOperationFilter
+ public class GeneratePathParamsValidationFilter : IOperationFilter
{
///
/// Constructor
diff --git a/samples/server/petstore/aspnetcore/src/IO.Swagger/Startup.cs b/samples/server/petstore/aspnetcore/src/IO.Swagger/Startup.cs
index e37b2adfc27..b62913042eb 100644
--- a/samples/server/petstore/aspnetcore/src/IO.Swagger/Startup.cs
+++ b/samples/server/petstore/aspnetcore/src/IO.Swagger/Startup.cs
@@ -80,9 +80,11 @@ namespace IO.Swagger
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();
+ c.DocumentFilter("/v2");
+
+ // Include DataAnnotation attributes on Controller Action parameters as Swagger validation rules (e.g required, pattern, ..)
+ // Use [ValidateModelState] on Actions to actually validate it in C# as well!
+ c.OperationFilter();
});
}