Egor Pavlikhin
Egor Pavlikhin

Reputation: 17981

Web API translate JSON object into simple parameters

If I am sending JSON data (via POST) to a .Net Core Web API like this

{ a: "a", b: "b" }

What do I need to do to have a controller method like this?

[HttpPost]
public async Task SometMethod(string a, string b) 
{
  return Ok();
}

Normally, all tutorials and docs say that you need to define a class and use [FromBody] attribute. But how can I make do without extra classes that I don't really need?

Upvotes: 1

Views: 478

Answers (3)

Egor Pavlikhin
Egor Pavlikhin

Reputation: 17981

After some research I came up with ModelBinder to do just this. It is not performant since it re-parses the whole request body for every parameter. I will improve it in the future.

https://github.com/egorpavlikhin/JsonParametersModelBinder

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

        var actionDescriptor = bindingContext.ActionContext.ActionDescriptor as ControllerActionDescriptor;
        if (actionDescriptor.MethodInfo.GetCustomAttributes(typeof(JsonParametersAttribute), false).Length > 0)
        {
            var context = bindingContext.HttpContext;
            if (context.Request.ContentType != "application/json")
            {
                bindingContext.Result = ModelBindingResult.Failed();
                return;
            }

#if (NETSTANDARD2_1 || NETCOREAPP3_0)
            context?.Request.EnableBuffering();
#else
        context?.Request.EnableRewind();
#endif

            using var reader = new StreamReader(context.Request.Body, Encoding.UTF8,
                false,
                1024,
                true); // so body can be re-read next time

            var body = await reader.ReadToEndAsync();
            var json = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(body);
            if (json.TryGetValue(bindingContext.FieldName, out var value))
            {
                if (bindingContext.ModelType == typeof(string))
                {
                    bindingContext.Result = ModelBindingResult.Success(value.GetString());
                }
                else if (bindingContext.ModelType == typeof(object))
                {
                    var serializerOptions = new JsonSerializerOptions
                    {
                        Converters = {new DynamicJsonConverter()}
                    };
                    var val = JsonSerializer.Deserialize<dynamic>(value.ToString(), serializerOptions);
                    bindingContext.Result = ModelBindingResult.Success(val);
                }
            }

            context.Request.Body.Position = 0; // rewind
        }
    }
}

Upvotes: 0

Rena
Rena

Reputation: 36645

Firstly,your json should be:

{ 
    "a":"a", 
    "b":"b" 
}

You could receive data as JObject instead of a class like below:

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    [HttpPost]
    public void Post(JObject data)
    {
        //get the property value like below
        var data1 = data["a"].ToString();
        var data2 = data["b"].ToString();
    }
}

Result (For easily distinguish value and property name,I change a to aaa and b to bbb): enter image description here

Upvotes: 1

Swagat Swain
Swagat Swain

Reputation: 505

If you want to post the data to the method like this, you will have to serialize your data before you can send it to the server. Assuming you are using JQuery, you can do like the following.

var postData = $.param({ a: "a", b: "b" });
//Then you can send this postData obejct to the server. This should perfectly bound to the parameters. 

You can also use the same in an angular app.

Upvotes: 0

Related Questions