1

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

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永远不会被调用。我在这里找到了针对此问题提出的另一个问题,遗憾的是没有答案。

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

我已经ComplexTypeModelBinderProvider源代码中复制了逻辑,令我惊讶的是BindModelAsync,我StringModelBinder现在收到了电话。但仍然失败,因为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,因为它已经过时了。

4

2 回答 2

0

FromBody不同于 theFromQuery和其他 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);
    }

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

在此处输入图像描述

把它放进去StringModelBinder

在此处输入图像描述

于 2021-01-04T06:20:54.677 回答
0

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

    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());
            });
于 2021-04-19T19:41:10.177 回答