Trim strings DRY

While we make comparison of local and cloud databases we have discovered few interesting issues, in some tables we had, for example emails with white spaces at begining or end.

We all humans, and it is just a matter of time when something like that happes, the question is - can we still deal with that

If such case may appear we always may create attribute like this and use it everywhere.

Also there is variant with custom model binder, but it will work only for primitive values

Also we can tune JsonConverter - then all strings in all json for input and output will be trimmed - profit (example below)

Custom model binder has one more benefit, because we can use it in scenarious when we want to cleanup html, xss, etc. but it will handle only new values, and variant with jsonconverter will work in both directions

In theory such converter should not broke anything, because at the very end we should trim everything by hands after all

Program.cs

using System;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;

public class Program
{
    public static void Main(string[] args) => WebHost
        .CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .Build()
        .Run();
}

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services
            .AddMvc()
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
            .AddJsonOptions(json => json.SerializerSettings.Converters.Add(new JsonStringsTrimmer()));
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseMvc();
    }
}

public class LoginModel
{
    public string Username { get; set; }
    public string Password { get; set; }
}

[ApiController]
public class ValuesController : ControllerBase
{
    // curl -s -X POST -H 'Content-Type: application/json' http://localhost:5000/login -d '{"username": " foo ", "password": " 123 "}'
    // {"username":"foo","password":"123"}
    [HttpPost]
    [Route(nameof(Login))]
    public LoginModel Login(LoginModel model)
    {
        System.Console.WriteLine(">>>>>>>>>>>>>>>>>>>>>");
        System.Console.WriteLine($"Username: '{model.Username}'"); // Username: 'foo'
        System.Console.WriteLine($"Password: '{model.Password}'"); // Password: '123'
        System.Console.WriteLine(">>>>>>>>>>>>>>>>>>>>>");
        return model;
    }
}

public class JsonStringsTrimmer : JsonConverter
{
    public override bool CanConvert(Type objectType) => objectType == typeof(string);

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => reader.TokenType == JsonToken.String && reader.Value != null
            ? (reader.Value as string).Trim()
            : reader.Value;

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value == null)
        {
            writer.WriteNull();
        }
        else
        {
            writer.WriteValue(value.ToString().Trim());
        }
    }
}