I Found a nice solution using custom model binder and custom JSON converter.
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 is my custom binder that transforms the string to a trimmed string using Trim()
method. To use this binder I made a custom IModelBinderProvider
that is is used to to resolve a binder for specified type (in my case string type).
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;
}
}
So I register CustomModelBinderProvider
like this:
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());
});
Until now everything is ok and all string models are trimmed except those which their binding source is request body ([FromBody]). Because default model binders uses System.Text.Json
namespace to convert request body to model types, instead of making a new binder that to the same job, I create a JSON converter that customize converting body to model types.
Here is my custom converter:
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);
}
}
And here is the way to use this converter:
services.AddControllers(opt =>
{
opt.ModelBinderProviders.Insert(0, new CustomModelBinderProvider());
})
.AddJsonOptions(opt =>
{
opt.JsonSerializerOptions.Converters.Add(new StringTrimmerJsonConverter());
});
That's it, now all my strings whether in complex-type or in simple-type will be trimmed.