Add new validation attribute framework

This commit is contained in:
honzapatCZ 2025-06-20 18:43:36 +02:00
parent 37477b4df0
commit 57ee784cc6
5 changed files with 160 additions and 0 deletions

View File

@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
using System.Numerics;
using System.Text;
using System.Text.RegularExpressions;
namespace NejCommon.Validation;
public class AlphanumericAttribute : RegexAttribute{
public AlphanumericAttribute() : base("^[a-zA-Z0-9]*$","Aabb123"){
}
}

View File

@ -0,0 +1,50 @@
using System.ComponentModel.DataAnnotations;
using System.Numerics;
using System.Text;
using System.Text.RegularExpressions;
namespace NejCommon.Validation;
public class IbanAttribute : RegexAttribute{
public const string IbanRegex = @"^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$";
public IbanAttribute() : base(IbanRegex,"CZ4622500000000104816258"){
}
protected override ValidationResult IsValid(object value, ValidationContext ctx)
{
if (value is null)
return ValidationResult.Success!;
var res = base.IsValid(value, ctx);
if (res != ValidationResult.Success)
return res;
string iban = value as string;
string rearranged = iban.Substring(4) + iban.Substring(0, 4);
// Convert letters to numbers (A=10, B=11, ..., Z=35)
StringBuilder numericIban = new StringBuilder();
foreach (char ch in rearranged)
{
if (char.IsLetter(ch))
{
int charVal = ch - 'A' + 10;
numericIban.Append(charVal);
}
else
{
numericIban.Append(ch);
}
}
// Convert to BigInteger and compute mod 97
BigInteger ibanNumber = BigInteger.Parse(numericIban.ToString());
if(ibanNumber % 97 == 1)
return ValidationResult.Success;
else
return new ValidationResult("Invalid checksum of IBAN");
}
}

View File

@ -0,0 +1,19 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.OpenApi.Models;
namespace NejCommon.Validation;
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public abstract class NejValidationAttribute : ValidationAttribute
{
public NejValidationAttribute(string errorMsg = null)
{
ErrorMessage = errorMsg ?? $"Invalid format";
}
public abstract string ExampleJson { get; }
public virtual void ApplySchema(OpenApiSchema schema) { }
}

View File

@ -0,0 +1,42 @@
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace NejCommon.Validation;
public class NejValidationAttributeFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
var type = context.Type;
var attrOnType = context.MemberInfo?.GetCustomAttributes(typeof(NejValidationAttribute), true)?.Select(a => a as NejValidationAttribute)?.FirstOrDefault();
if (attrOnType != null)
{
Console.WriteLine("Added examples for: " + context.MemberInfo?.Name);
schema.Example = OpenApiAnyFactory.CreateFromJson(attrOnType.ExampleJson);
attrOnType.ApplySchema(schema);
}
var attrOnType2 = context.ParameterInfo?.GetCustomAttributes(typeof(NejValidationAttribute), true)?.Select(a => a as NejValidationAttribute)?.FirstOrDefault();
if (attrOnType2 != null)
{
Console.WriteLine("Added examples for: " + context.ParameterInfo?.Name);
schema.Example = OpenApiAnyFactory.CreateFromJson(attrOnType2.ExampleJson);
attrOnType.ApplySchema(schema);
}
foreach (var prop in type.GetProperties())
{
if (schema.Properties.TryGetValue(prop.Name, out var pschema))
{
Console.WriteLine("Schema for: " + prop.Name);
var attr = prop.GetCustomAttributes(typeof(NejValidationAttribute), true).Select(a => a as NejValidationAttribute).FirstOrDefault();
if (attr != null)
{
Console.WriteLine("Added examples for: " + prop.Name);
pschema.Example = OpenApiAnyFactory.CreateFromJson(attr.ExampleJson);
attrOnType.ApplySchema(schema);
}
}
}
}
}

View File

@ -0,0 +1,36 @@
using System.ComponentModel.DataAnnotations;
using System.Text.Json;
using System.Text.RegularExpressions;
using Microsoft.OpenApi.Models;
namespace NejCommon.Validation;
public class RegexAttribute : NejValidationAttribute
{
public override string ExampleJson { get; }
public string Pattern { get; }
public RegexAttribute(string pattern, string example, string errorMsg = null)
{
Pattern = pattern;
ExampleJson = JsonSerializer.Serialize(example);
ErrorMessage = errorMsg ?? $"Invalid format (must match {pattern})";
}
protected override ValidationResult IsValid(object value, ValidationContext ctx)
{
if (value is null)
return ValidationResult.Success!;
var str = value as string;
if (string.IsNullOrWhiteSpace(str) || !Regex.IsMatch(str, Pattern))
return new ValidationResult(ErrorMessage);
return ValidationResult.Success!;
}
public override void ApplySchema(OpenApiSchema schema)
{
base.ApplySchema(schema);
schema.Pattern = Pattern;
}
}