Surgical Coder Surgical Coder - 2 months ago 14
C# Question

"Unwrapping" posted JSON

I'm looking for a way to "unwrap" JSON that's been posted to a MVC Core service. Let's say, I have the following method:

[HttpPost]
public dynamic SayHello(string FirstName, string SecondName)
{
return $"Hello {FirstName} {SecondName} !";
}


And I post in the following JSON:

{
"FirstName":"Joe",
"SecondName": "Bloggs"
}


I'd expect to get a response of
Hello Joe Bloggs !
, but I cannot seem to find an easy way to unwrap the JSON object into the properties of the method.

I know the correct solution is to have a
HelloModel
with those two properties in, slap on a
[FromBody]
attribute, but for reasons this isn't possible.

Answer Source

Here's another (slightly convoluted) option. You can create your own action filter to intercept the request and populate the parameters with values based on deciding the JSON yourself. For example:

public class JsonToParametersAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);

        var stream = filterContext.HttpContext.Request.Body;
        using (var sr = new StreamReader(stream))
        using (var jsonTextReader = new JsonTextReader(sr))
        {
            var serializer = new JsonSerializer();
            var body = serializer.Deserialize<JObject>(jsonTextReader);

            if (body == null) return;

            foreach (var parameter in filterContext.ActionDescriptor.Parameters)
            {
                var jsonProperty = body.Properties().SingleOrDefault(p => p.Name == parameter.Name);

                if (jsonProperty != null)
                {
                    var param = filterContext.ActionDescriptor.Parameters.OfType<ControllerParameterDescriptor>().FirstOrDefault(e => e.Name == parameter.Name);

                    if (param == null)
                    {
                        continue;
                    }

                    if (!filterContext.ActionArguments.ContainsKey(parameter.Name))
                    {
                        object value;

                        try
                        {
                            value = jsonProperty.Value.ToObject(param.ParameterInfo.ParameterType);
                        }
                        catch (Exception)
                        {
                            value = GetDefault(param.ParameterInfo.ParameterType);
                        }

                        filterContext.ActionArguments.Add(parameter.Name, value);
                    }
                }
            }
        }
    }
    private static object GetDefault(Type type)
    {
        if (type.IsValueType)
        {
            return Activator.CreateInstance(type);
        }
        return null;
    }
}

Now decorate your action method with this new attribute:

[HttpPost]     
[JsonToParameters]   
public dynamic SayHello(string FirstName, string SecondName)
{
    return $"Hello {FirstName} {SecondName} !";
}