在 Asp.Net Core 中进行模型验证之前修剪字符串

Moh*_*ast 4 c# asp.net-core

我有一个从主体源绑定的 DTO 类来创建我的用户:

    public class UserDto
    {
        [Required()]
        [MinLength(2)]
        [MaxLength(30)]
        public string FirstName { get; set; }

        [Required()]
        [MinLength(2)]
        [MaxLength(30)]
        public string LastName { get; set; }

        [Required]
        [SocialSerialNumber]
        public string SSN { get; set; }

        [Required]
        [PhoneNumber]
        public string PhoneNumber { get; set; }


        [Required]
        public bool? Gender { get; set; }  
        [Required]
        [MinLength(6)]
        [MaxLength(30)]
        [IgnoreTrim]  // this is what I need
        public string Password { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)

我想在验证之前修剪所有模型中的所有字符串(删除多余的空格)。对于我明确指定为 no-trim 的字符串,必须忽略修剪(可能使用名为 的属性[IgnoreTrim])。

在上面的示例中,需要对属性进行修剪,FirstName但不需要。LastNamePhoneNumberPassword

我知道我可以在控制器操作中手动修剪它们,然后重新验证模型,但我正在寻找优雅的方法来执行此操作,以便我的操作保持干净。

Moh*_*ast 6

我使用自定义模型绑定器自定义 JSON 转换器找到了一个很好的解决方案。

public class StringTrimmerBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext is null)
            throw new ArgumentNullException(nameof(bindingContext));

        var modelName = bindingContext.ModelName;
        var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);

        if (valueProviderResult == ValueProviderResult.None)
            return Task.CompletedTask;

        bindingContext.Result = ModelBindingResult.Success(valueProviderResult.FirstValue.Trim());
        return Task.CompletedTask;
    }
}
Run Code Online (Sandbox Code Playgroud)

StringTrimmerBinder 是我的自定义活页夹,它使用方法将字符串转换为修剪后的字符串Trim()。为了使用这个活页夹,我做了一个自定义IModelBinderProvider,用于解析指定类型的活页夹(在我的例子中是字符串类型)。

public class CustomModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context is null)
            throw new ArgumentNullException(nameof(context));


        if (context.Metadata.ModelType == typeof(string) && context.BindingInfo.BindingSource!=BindingSource.Body)
            return new StringTrimmerBinder();

        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

所以我CustomModelBinderProvider这样注册:

services.AddControllers(opt =>
{
    //registers CustomModelBinderProvider in the first place so that it is asked before other model binder providers.
    opt.ModelBinderProviders.Insert(0, new CustomModelBinderProvider());
});
Run Code Online (Sandbox Code Playgroud)

到目前为止,一切正常,所有字符串模型都被修剪,除了那些绑定源是请求正文([FromBody])的字符串模型。由于默认模型绑定程序使用System.Text.Json命名空间将请求正文转换为模型类型,因此我创建了一个 JSON 转换器来自定义将正文转换为模型类型,而不是为同一作业创建新的绑定程序。

这是我的自定义转换器:

public class StringTrimmerJsonConverter : JsonConverter<string>
{
    public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return reader.GetString().Trim();
    }

    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是使用此转换器的方法:

services.AddControllers(opt =>
{
    opt.ModelBinderProviders.Insert(0, new CustomModelBinderProvider());
})
    .AddJsonOptions(opt =>
    {
        opt.JsonSerializerOptions.Converters.Add(new StringTrimmerJsonConverter());
    });
Run Code Online (Sandbox Code Playgroud)

就是这样,现在我的所有字符串,无论是复杂类型还是简单类型都将被修剪。