使用 Binding ASP.Net Core 5 规范化字符串输入
Normalize string inputs with Binding ASP.Net Core 5
我需要对字符串数据进行规范化(将一些字符相互替换,例如:将“ь”替换为“ي”或trim)。为此,我创建了如下模型活页夹:
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;
}
}
此活页夹适用于 Query
和 Route
绑定,但如果我使用 FromBody
属性则失败。它失败了,因为 BindModelAsync
永远不会被调用。我发现了针对此问题提出的另一个问题 here,遗憾的是没有答案。
我试图扩展 ComplexObjectModelBinder
但它是一个 sealed
class (并且也不提供任何构造函数)。所以我尝试扩展 ComplexTypeModelBinder
,它被注释为已过时。
我已经从 source code 复制了 ComplexTypeModelBinderProvider
的逻辑,令我惊讶的是,我的 StringModelBinder
的 BindModelAsync
现在可以收到呼叫。但仍然失败,因为 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。 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);
}
然后,将字符串传递给动作。
在StringModelBinder
.
中获取
您可以实现自定义字符串转换器:
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());
});
我需要对字符串数据进行规范化(将一些字符相互替换,例如:将“ь”替换为“ي”或trim)。为此,我创建了如下模型活页夹:
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;
}
}
此活页夹适用于 Query
和 Route
绑定,但如果我使用 FromBody
属性则失败。它失败了,因为 BindModelAsync
永远不会被调用。我发现了针对此问题提出的另一个问题 here,遗憾的是没有答案。
我试图扩展 ComplexObjectModelBinder
但它是一个 sealed
class (并且也不提供任何构造函数)。所以我尝试扩展 ComplexTypeModelBinder
,它被注释为已过时。
我已经从 source code 复制了 ComplexTypeModelBinderProvider
的逻辑,令我惊讶的是,我的 StringModelBinder
的 BindModelAsync
现在可以收到呼叫。但仍然失败,因为 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。 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);
}
然后,将字符串传递给动作。
在StringModelBinder
.
您可以实现自定义字符串转换器:
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());
});