首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在Asp.Net核心中进行模型验证之前修剪字符串

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

Stack Overflow用户
提问于 2021-09-06 12:29:04
回答 4查看 2K关注 0票数 3

我有一个DTO类,它从body源绑定来创建我的用户:

代码语言:javascript
运行
复制
    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; }
    }

在验证之前,我想在所有模型中修剪(从所有模型中删除额外的空格)所有字符串。对于我显式指定为no的字符串,必须忽略修整(可能使用一个名为[IgnoreTrim]的属性)。

在上面的示例中,需要对属性FirstNameLastNamePhoneNumber进行修整,但不需要Password

我知道我可以在控制器操作中手动修剪它们,然后重新验证模型,但是我正在寻找一种优雅的方法,这样我的操作才能保持干净。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2021-09-08 13:55:10

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

代码语言:javascript
运行
复制
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;
    }
}

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

代码语言:javascript
运行
复制
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;
    }
}

所以我这样注册CustomModelBinderProvider

代码语言:javascript
运行
复制
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());
});

到目前为止,一切都是正常的,所有字符串模型都被裁剪了,除了那些绑定源是请求体(FromBody)的模型。因为默认模型绑定使用System.Text.Json命名空间将请求体转换为模型类型,所以我创建了一个JSON转换器,它自定义将主体转换为模型类型,而不是制作一个新的绑定器。

这是我的自定义转换器:

代码语言:javascript
运行
复制
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);
    }
}

下面是使用这个转换器的方法:

代码语言:javascript
运行
复制
services.AddControllers(opt =>
{
    opt.ModelBinderProviders.Insert(0, new CustomModelBinderProvider());
})
    .AddJsonOptions(opt =>
    {
        opt.JsonSerializerOptions.Converters.Add(new StringTrimmerJsonConverter());
    });

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

票数 4
EN

Stack Overflow用户

发布于 2021-09-06 12:41:42

更新

我已经包含了两个模型绑定程序,它们与JSON一起工作,并从主体中形成字段数据。

实现IModelBinder并执行以下操作:

来自Json的绑定:

代码语言:javascript
运行
复制
public class CustomModelBinderJson : IModelBinder
{
    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var request = bindingContext.HttpContext.Request;
        string body;
        using (StreamReader sr = new StreamReader(request.Body, Encoding.UTF8))
        {
            body = await sr.ReadToEndAsync();
        }

        var dataModel = JsonSerializer.Deserialize<DataModel>(body);

        dataModel.Name = dataModel.Name.Trim();

        bindingContext.Result = ModelBindingResult.Success(dataModel);
    }
}

或从表单字段绑定:

代码语言:javascript
运行
复制
public class CustomModelBinderForm : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var request = bindingContext.HttpContext.Request;
        var form = request.Form["Name"];
        var id = request.Form["Id"];
        var dataModel = new DataModel();

        dataModel.Id = int.Parse(id);
        dataModel.Name = form.ToString().Trim();

        bindingContext.Result = ModelBindingResult.Success(dataModel);
        return Task.CompletedTask;
    }
}

然后

代码语言:javascript
运行
复制
 [ModelBinder(BinderType = typeof(CustomModelBinderForm))] //Or use the CustomModelBinderJson
    public class DataModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

你的行动应该是这样的:

代码语言:javascript
运行
复制
[HttpPost]
public IActionResult Index([FromBody] DataModel dataModel)
{
    //Do stuff
}

请参阅MSDocs

票数 2
EN

Stack Overflow用户

发布于 2021-09-06 19:56:49

您可以使用getter来修剪将存储在私有字段中的值。这样,您的验证将针对已修剪的值进行。

例如,使用属性FirstName

代码语言:javascript
运行
复制
public class UserDto
{
    private string firstName;

    [Required()]
    [PersianChars]
    [MinLength(2)]
    [MaxLength(30)]
    public string FirstName 
    {
        get 
        {
            return firstName?.Trim(); 
        }
        set 
        {
            firstName = value;
        }
    }
}

另一种解决方案是实现一个自定义属性,该属性指示在验证之前需要修剪哪些属性。此时,您可以将此过程自动化。

要使用自定义属性自动调整属性的值,请执行以下操作:

您需要创建一个自定义属性

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/attributes/creating-custom-attributes

代码语言:javascript
运行
复制
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class TrimmedValueAttribute : Attribute
{
}

然后,将属性添加到需要的属性,例如FirstName

代码语言:javascript
运行
复制
public class UserDto
{
    [Required()]
    [PersianChars]
    [MinLength(2)]
    [MaxLength(30)]
    [TrimmedValue]
    public string FirstName { get; set }
}

之后,您需要创建一个操作筛选器,它自动调整具有TrimmedValueAttribute的属性

https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-5.0#action-filters

代码语言:javascript
运行
复制
public class TrimPropertiesActionFilter : IActionFilter 
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        Use reflection to find all the properties which has the TrimValueAttribute 
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
    }
}

然后,在ConfigureServices方法上添加过滤器

代码语言:javascript
运行
复制
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
    {
        options.Filters.Add(typeof(TrimPropertiesActionFilter));
    });
}

可以通过设置Add方法的第二个参数来更改操作筛选器的顺序。

代码语言:javascript
运行
复制
options.Filters.Add(typeof(TrimPropertiesActionFilter, order: 0));
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69074572

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档