mirror of
https://github.com/valitydev/openapi-generator.git
synced 2024-11-07 10:58:55 +00:00
Added support for controller action parameter validation into the ValidateModelStateAttribute (#7156)
This commit is contained in:
parent
5306372b98
commit
a96bc3a409
@ -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("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 + "BasePathFilter.mustache", packageFolder + File.separator + "Filters", "BasePathFilter.cs"));
|
||||||
supportingFiles.add(new SupportingFile("Filters" + File.separator + "PathParameterValidationFilter.mustache", packageFolder + File.separator + "Filters", "PathParameterValidationFilter.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 + "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 + "index.html", packageFolder + File.separator + "wwwroot", "index.html"));
|
||||||
|
@ -8,13 +8,13 @@ namespace {{packageName}}.Filters
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// BasePath Document Filter sets BasePath property of Swagger and removes it from the individual URL paths
|
/// BasePath Document Filter sets BasePath property of Swagger and removes it from the individual URL paths
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class BasePathDocumentFilter : IDocumentFilter
|
public class BasePathFilter : IDocumentFilter
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="basePath">BasePath to remove from Operations</param>
|
/// <param name="basePath">BasePath to remove from Operations</param>
|
||||||
public BasePathDocumentFilter(string basePath)
|
public BasePathFilter(string basePath)
|
||||||
{
|
{
|
||||||
BasePath = basePath;
|
BasePath = basePath;
|
||||||
}
|
}
|
@ -7,9 +7,9 @@ using Swashbuckle.AspNetCore.SwaggerGen;
|
|||||||
namespace {{packageName}}.Filters
|
namespace {{packageName}}.Filters
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Path Parameter Validation Filter
|
/// Path Parameter Validation Rules Filter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PathParameterValidationFilter : IOperationFilter
|
public class GeneratePathParamsValidationFilter : IOperationFilter
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor
|
/// Constructor
|
@ -72,10 +72,12 @@ namespace {{packageName}}
|
|||||||
c.IncludeXmlComments($"{AppContext.BaseDirectory}{Path.DirectorySeparatorChar}{_hostingEnv.ApplicationName}.xml");
|
c.IncludeXmlComments($"{AppContext.BaseDirectory}{Path.DirectorySeparatorChar}{_hostingEnv.ApplicationName}.xml");
|
||||||
{{#basePathWithoutHost}}
|
{{#basePathWithoutHost}}
|
||||||
// Sets the basePath property in the Swagger document generated
|
// Sets the basePath property in the Swagger document generated
|
||||||
c.DocumentFilter<BasePathDocumentFilter>("{{{basePathWithoutHost}}}");
|
c.DocumentFilter<BasePathFilter>("{{{basePathWithoutHost}}}");
|
||||||
{{/basePathWithoutHost}}
|
{{/basePathWithoutHost}}
|
||||||
// Do validation of path parameters w. DataAnnotation attributes
|
|
||||||
c.OperationFilter<PathParameterValidationFilter>();
|
// 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<GeneratePathParamsValidationFilter>();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Reflection;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
|
||||||
namespace {{packageName}}.Attributes
|
namespace {{packageName}}.Attributes
|
||||||
{
|
{
|
||||||
@ -14,10 +18,44 @@ namespace {{packageName}}.Attributes
|
|||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
public override void OnActionExecuting(ActionExecutingContext context)
|
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)
|
if (!context.ModelState.IsValid)
|
||||||
{
|
{
|
||||||
context.Result = new BadRequestObjectResult(context.ModelState);
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Reflection;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
|
||||||
namespace IO.Swagger.Attributes
|
namespace IO.Swagger.Attributes
|
||||||
{
|
{
|
||||||
@ -14,10 +18,44 @@ namespace IO.Swagger.Attributes
|
|||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
public override void OnActionExecuting(ActionExecutingContext context)
|
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)
|
if (!context.ModelState.IsValid)
|
||||||
{
|
{
|
||||||
context.Result = new BadRequestObjectResult(context.ModelState);
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,13 @@ namespace IO.Swagger.Filters
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// BasePath Document Filter sets BasePath property of Swagger and removes it from the individual URL paths
|
/// BasePath Document Filter sets BasePath property of Swagger and removes it from the individual URL paths
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class BasePathDocumentFilter : IDocumentFilter
|
public class BasePathFilter : IDocumentFilter
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="basePath">BasePath to remove from Operations</param>
|
/// <param name="basePath">BasePath to remove from Operations</param>
|
||||||
public BasePathDocumentFilter(string basePath)
|
public BasePathFilter(string basePath)
|
||||||
{
|
{
|
||||||
BasePath = basePath;
|
BasePath = basePath;
|
||||||
}
|
}
|
@ -7,9 +7,9 @@ using Swashbuckle.AspNetCore.SwaggerGen;
|
|||||||
namespace IO.Swagger.Filters
|
namespace IO.Swagger.Filters
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Path Parameter Validation Filter
|
/// Path Parameter Validation Rules Filter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PathParameterValidationFilter : IOperationFilter
|
public class GeneratePathParamsValidationFilter : IOperationFilter
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor
|
/// Constructor
|
@ -80,9 +80,11 @@ namespace IO.Swagger
|
|||||||
c.DescribeAllEnumsAsStrings();
|
c.DescribeAllEnumsAsStrings();
|
||||||
c.IncludeXmlComments($"{AppContext.BaseDirectory}{Path.DirectorySeparatorChar}{_hostingEnv.ApplicationName}.xml");
|
c.IncludeXmlComments($"{AppContext.BaseDirectory}{Path.DirectorySeparatorChar}{_hostingEnv.ApplicationName}.xml");
|
||||||
// Sets the basePath property in the Swagger document generated
|
// Sets the basePath property in the Swagger document generated
|
||||||
c.DocumentFilter<BasePathDocumentFilter>("/v2");
|
c.DocumentFilter<BasePathFilter>("/v2");
|
||||||
// Do validation of path parameters w. DataAnnotation attributes
|
|
||||||
c.OperationFilter<PathParameterValidationFilter>();
|
// 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<GeneratePathParamsValidationFilter>();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user