使用 Binding ASP.Net Core 5 现在的问题是,在 .Net 5 中进行这种标准化的最佳方法是什么?

问题描述

我需要对字符串数据进行规范化(将一些字符相互替换,例如:'ی' 与 'ي' 或修剪它)。为此,我创建了以下模型绑定器,如下所示:

public class StringModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

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

        var value = Normalize(valueProviderResult.FirstValue);

        bindingContext.Result = ModelBindingResult.Success(value);

        return Task.CompletedTask;
    }
}

此活页夹适用于 QueryRoute 绑定,但如果我使用 FromBody 属性则失败。它失败是因为 BindModelAsync 永远不会被调用。我发现针对此问题 here 提出的另一个问题,遗憾的是没有答案。

我尝试扩展 ComplexObjectModelBinder 但它是一个 sealed 类(并且也不提供任何构造函数)。所以我尝试扩展 ComplexTypeModelBinder,它被注释为已过时。

我已经从 source code 复制了 ComplexTypeModelBinderProvider 的逻辑,令我惊讶的是,我的 BindModelAsyncStringModelBinder 现在收到了调用。但仍然失败,因为 bindingContext.ValueProvider 只包含一个路由提供者,结果仍然为空。

我现阶段的活页夹提供者:

public class MyModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType)
        {
            var propertyBinders = new Dictionary<ModelMetadata,IModelBinder>();
            for (var i = 0; i < context.Metadata.Properties.Count; i++)
            {
                var property = context.Metadata.Properties[i];
                propertyBinders.Add(property,context.CreateBinder(property));
            }
                
            var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>();
            return new ComplexTypeModelBinder(
                propertyBinders,loggerFactory,allowValidatingTopLevelNodes: true);
        }

        if (context.Metadata.ModelType == typeof(string))
        {
            return new StringModelBinder();
        }

        return null;
    }
}

我还尝试从正文创建一个提供程序并将我的 StringModelBinder 更改为:

public class StringModelBinder : IModelBinder
{
    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

        if (valueProviderResult == ValueProviderResult.None)
        {
            var context = new ValueProviderFactoryContext(bindingContext.ActionContext);
            await new FormValueProviderFactory().CreateValueProviderAsync(context);

            valueProviderResult = context.ValueProviders
                .Select(x => x.GetValue(bindingContext.ModelName))
                .FirstOrDefault(x => x != ValueProviderResult.None);

            if (valueProviderResult == ValueProviderResult.None) return;
        }

        var value = valueProviderResult.FirstValue.Replace("A","B");

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

现在的问题是,在 .Net 5 中进行这种标准化的最佳方法是什么?

可能关心的人:这个问题可能看起来重复,但我找不到与 .Net 5 相关的任何内容,如果有问题回答 ComplexTypeModelBinder 问题,它将不适合 .Net 5因为它已经过时了。

解决方法

FromBody 不同于 FromQuery 和其他 HTTP 动词。

在复杂模型绑定 (FromBody) 中,您可以在 bindingContext.HttpContext.Request.Body 中获取它们。

public class StringModelBinder : IModelBinder
{
    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        
        using (var reader = new StreamReader(bindingContext.HttpContext.Request.Body))
        {
            var body = reader.ReadToEndAsync();
            var mydata = body.Result;

            //...
            bindingContext.Result = ModelBindingResult.Success(mydata);
        }
        //...
    }
}

动作

    [HttpPost]
    public IActionResult test1([ModelBinder(binderType: typeof(StringModelBinder))]string model)
    {

        return Ok(model);
    }

然后,将字符串传递给操作。

enter image description here

StringModelBinder获取。

enter image description here

,

您可以实现自定义字符串转换器:

    public class CustomStringConverter : System.Text.Json.Serialization.JsonConverter<string>
{
    public override string Read(ref Utf8JsonReader reader,Type typeToConvert,JsonSerializerOptions options)
    {
        var value = reader.GetString();

        // Do your stuffs with value

        return value;
    }

    public override void Write(Utf8JsonWriter writer,string value,JsonSerializerOptions options)
    {
        writer.WriteStringValue(value);
    }
}

然后注册:

services.AddControllers()
            .AddJsonOptions(options =>
            {
                options.JsonSerializerOptions.Converters.Add(new CustomStringConverter());
            });

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...