C#中如何为类内自定义类型CustomString添加自定义ModelBinder?
自定义类型绑定请求体字段并通过DI控制格式的解决方案
问题描述
定义了WarehouseDTO类,其中Name字段为自定义类型CustomString,尝试通过自定义ModelBinder绑定请求体中的该字段,但调试发现ModelBinderProvider仅识别到WarehouseDTO的类型,内部CustomString字段未触发绑定逻辑。同时希望通过依赖注入决定CustomString的字符串格式(如大写、标题大小写)。
相关代码如下:
控制器代码
public async Task<IActionResult> Create([FromBody] WarehouseDTO warehouseDTO)
自定义ModelBinder与Provider代码
using Microsoft.AspNetCore.Mvc.ModelBinding; public class CustomStringModelBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } if (bindingContext.BindingSource == BindingSource.Body) { var fieldName = bindingContext.FieldName; string val = bindingContext.ValueProvider.GetValue(fieldName).FirstValue; if (val == null) { val = bindingContext.ValueProvider.GetValue("Value").FirstValue; } bindingContext.Result = ModelBindingResult.Success(new CustomString(val)); } return Task.CompletedTask; } } public class CustomStringBinderProvider : IModelBinderProvider { public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (context.Metadata.ModelType == typeof(CustomString)) { return new CustomStringModelBinder(); } return null; } }
WarehouseDTO代码
public class WarehouseDTO { public int Id { get; set; } [ModelBinder(typeof(CustomStringModelBinder))] public CustomString Name { get; set; } // 其他字段... }
解决方案
问题根源
使用[FromBody]特性时,ASP.NET Core默认通过JSON序列化器(如System.Text.Json或Newtonsoft.Json)直接反序列化整个请求体,不会触发ModelBinder系统,因此自定义的CustomStringModelBinder无法作用到WarehouseDTO内部的CustomString字段。
正确实现方式:自定义JSON转换器+依赖注入
通过自定义JSON转换器处理CustomString的反序列化,同时利用依赖注入注入格式控制服务,实现动态格式调整。
1. 定义格式控制接口与实现
先定义格式控制接口,以及具体的格式实现:
// 格式控制接口 public interface IStringFormatter { string Format(string input); } // 示例:大写格式实现 public class UppercaseStringFormatter : IStringFormatter { public string Format(string input) { return input?.ToUpperInvariant(); } } // 示例:标题格式实现 public class TitleCaseStringFormatter : IStringFormatter { public string Format(string input) { if (string.IsNullOrEmpty(input)) return input; return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(input.ToLower()); } }
2. 修改CustomString类,依赖格式接口
让CustomString通过构造函数注入IStringFormatter,由格式器处理字符串格式:
public class CustomString { public string Value { get; } public CustomString(string value, IStringFormatter formatter) { Value = formatter.Format(value); } }
3. 实现自定义JSON转换器
创建继承自JsonConverter<CustomString>的转换器,在反序列化时使用注入的格式器:
public class CustomStringJsonConverter : JsonConverter<CustomString> { private readonly IStringFormatter _formatter; // 通过构造函数注入格式器 public CustomStringJsonConverter(IStringFormatter formatter) { _formatter = formatter; } public override CustomString Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var rawValue = reader.GetString(); return new CustomString(rawValue, _formatter); } public override void Write(Utf8JsonWriter writer, CustomString value, JsonSerializerOptions options) { writer.WriteStringValue(value.Value); } }
4. 注册服务与转换器
在Program.cs(或Startup.cs)中注册格式器服务和自定义JSON转换器:
var builder = WebApplication.CreateBuilder(args); // 注册格式器服务(可根据配置切换不同实现) builder.Services.AddSingleton<IStringFormatter, UppercaseStringFormatter>(); // 配置控制器并添加自定义JSON转换器 builder.Services.AddControllers() .AddJsonOptions(options => { // 从服务容器获取格式器实例 var serviceProvider = builder.Services.BuildServiceProvider(); var formatter = serviceProvider.GetRequiredService<IStringFormatter>(); options.JsonSerializerOptions.Converters.Add(new CustomStringJsonConverter(formatter)); }); var app = builder.Build(); app.MapControllers(); app.Run();
5. 调整WarehouseDTO
移除[ModelBinder]特性,因为不再需要ModelBinder:
public class WarehouseDTO { public int Id { get; set; } public CustomString Name { get; set; } // 其他字段... }
说明
- 该方式直接参与JSON反序列化流程,完美适配
[FromBody]场景。 - 通过注册不同的
IStringFormatter实现,可以动态切换CustomString的格式规则,满足依赖注入的需求。
内容的提问来源于stack exchange,提问作者Sora Teichman
相关产品推荐
相关产品推荐

