SpiritBob
SpiritBob

Reputation: 2662

Custom model binder for QueryString string parameters in ASP.NET Core 3.1?

I'd like to implement some/any custom behavior for some data type, for example DateTime or int.

I've created a custom JsonConverter which encompasses data received from the body of a request (unless it was specified as non-json), which allows me to do just that.

But if data is passed in the query string of a request, for example ?param1=helloWorld&param2=123", they are handled differently and are not covered by my custom JsonConverter.

I've read about creating/implementing my own Custom Model Binder, but those from the looks of it are for complex types, so I'm a bit lost on how exactly I can modify an incoming query string parameter, or if that's not possible - gain access to the whole query string, searching for the parameters I want to modify, and modifying those. (decoupled from Action methods, similar to filters and whatnot.)

Thank you!

Upvotes: 4

Views: 4537

Answers (1)

Fei Han
Fei Han

Reputation: 27793

creating/implementing my own Custom Model Binder, but those from the looks of it are for complex types, so I'm a bit lost on how exactly I can modify an incoming query string parameter

You can create and apply custom model binder to simple types parameter(s), like below.

public IActionResult Test(string param1, [ModelBinder(BinderType = typeof(Param2ModelBinder))]int param2)
{

decoupled from Action methods, similar to filters and whatnot.

If you do not want to directly apply custom model binder to action parameter, you can implement a custom model binder provider and specify the parameter your binder operates on, then add it to MVC's collection of providers.

Param2ModelBinder class

public class Param2ModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        // ...
        // implement it based on your actual requirement
        // code logic here
        // ...

        var model = 0;

        if (bindingContext.ValueProvider.GetValue("param2").FirstOrDefault() != null)
        {
            model = JsonSerializer.Deserialize<int>(bindingContext.ValueProvider.GetValue("param2").FirstOrDefault());

            // just for testing purpose
            // if received data > 100
            // set it to 100

            if ((int)model > 100)
            {
                model = 100;
            }
        }


        bindingContext.Result = ModelBindingResult.Success(model);
        return Task.CompletedTask;
    }
}

MyCustomBinderProvider class

public class MyCustomBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        // specify the parameter your binder operates on
        if (context.Metadata.ParameterName == "param2")
        {
            return new BinderTypeModelBinder(typeof(Param2ModelBinder));
        }

        return null;
    }
}

Add custom model binder provider

services.AddControllersWithViews(opt=> {
    opt.ModelBinderProviders.Insert(0, new MyCustomBinderProvider());
});

Test Result

enter image description here

Upvotes: 4

Related Questions