简体   繁体   中英

How to pass Javascript dictionary to controller where C# dictionary value is an object

Imagine this Javascript dictionary:

var things = {};
things['1'] = 10;
things['2'] = 11;

Here's a little bit of ajax code:

$.ajax({
    url: '/Controller/Foo',
    type: 'POST',
    data: {
        things: things
    },

Here's something that works:

[HttpPost]
public IActionResult Foo(Dictionary<string, int> things)

things will show that 1 maps to 10 and 2 maps to 11.

Here's something that DOES NOT work:

[HttpPost]
public IActionResult Foo(Dictionary<string, object> things)

things will show that 1 maps to null and 2 maps to null.

I can not change the Dictionary types. In reality, that Dictionary is part of a complex object that is used throughout the application (on the C# side of things). What you are looking at is a dumbed-down example.

In addition, JSON.stringify does not help at all. In fact, if I stringify the dictionary I get a count of 0, with no values.

Using C# to express my point, I think the expectation is more this (than what is currently happening):

int x = 5;
object foo = (object)x;

The dictionary was defined like this because one could be doing:

things[key] = 1;

or

things[key] = "string";

that's why it was declared as an object.

I am using ASP.NET Core if that matters (and jquery 3.4.1).

Thank you.

You could custom a DictionaryModelBinder like below :

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

        var result = new Dictionary<string, object> {};
        var form = bindingContext.HttpContext.Request.Form;
        if (form==null)
        {
            bindingContext.ModelState.AddModelError("FormData", "The data is null");
            return Task.CompletedTask;
        }
        foreach ( var k in form.Keys){
            StringValues v = string.Empty;
            var flag = form.TryGetValue(k, out v);
            if (flag)
            { 
                result.Add(k, v );
            }
        }

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

Controller :

[HttpPost]
    public IActionResult Foo([ModelBinder(BinderType = typeof(DictionaryModelBinder))]Dictionary<string, object> things)
    {
        // the stuff you want
    }

I don't think you can bind to object as a parameter (or Dictionary value type) because:

A complex type must have a public default constructor and public writable properties to bind. When model binding occurs, the class is instantiated using the public default constructor.

I think you could either strongly type your object (which sounds like it's not an option for you) or change your endpoint to take all its relevant properties distinctly as simple types ( string , bool , int , etc.).

Reference: Model Binding in ASP.NET Core

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM