0

I'm working on a .NET core web API application, where I want the URL routing values into Model/FromBody parameter. If the routing property is exist in From body model.

Is there any generic solution for this, i.e will work for all model types.

We already found custom model binder, but it working for specific to model type. I'm looking for custom model binder which is working for all model types.

for example:

Action route: [Route("AH/{userId}")]
Url : ../AH/123
From body: 
{
"userId":"",
"Value" : "Some value"
}

Now the routing value 123 needs to mapped to FromBody model property "userId"

Magendran V
  • 1,411
  • 3
  • 19
  • 33

2 Answers2

0

You can't fill a route param from the request body. The route param is part of the URL, and must exist therefore independently of what body is actually posted.

Regardless, you should never trust something like an ID coming from a post request body. Always get that information from the URL. The URL defines a unique resource, so that post to that URL can only ever affect that one unique resource. If you rely on the post body, then a malicious user can manipulate the value that's posted to potentially alter resources they should not have access to.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
0

For your current request, the "userId":"" will make request fail since "" could not be converted to int value.

For a workaround to achieve your requirement, you could modify the request body before model binding with steps below:

  1. ModelResourceFilterAttribute

    public class ModelResourceFilterAttribute : Attribute, IResourceFilter
    {
        public void OnResourceExecuted(ResourceExecutedContext context)
        {
        }
    
        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            context.HttpContext.Request.EnableRewind();
            var routeData = context.RouteData;
            var stream = context.HttpContext.Request.Body;
            using (var streamReader = new StreamReader(context.HttpContext.Request.Body))
            {
                var json = streamReader.ReadToEnd();
                if (json != "")
                {
                    var jsonObj = JObject.Parse(json);
                    foreach (var item in routeData.Values)
                    {
                        JToken token;
                        if (jsonObj.TryGetValue(
                            item.Key,
                            StringComparison.InvariantCultureIgnoreCase,
                            out token))
                        {
                            var jProperty = token.Parent as JProperty;
                            if (jProperty != null)
                            {
                                jProperty.Value = item.Value.ToString();
                            }
                        }
                    }
                    var body = jsonObj.ToString(Formatting.Indented);
                    byte[] byteArray = Encoding.UTF8.GetBytes(body);
                    //byte[] byteArray = Encoding.ASCII.GetBytes(contents);
                    context.HttpContext.Request.Body = new MemoryStream(byteArray);
                }
    
            }            
        }
    }
    
  2. Register ModelResourceFilterAttribute

    services.AddMvc(options =>
    {
        options.Filters.Add(typeof(ModelResourceFilterAttribute));
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    
Edward
  • 28,296
  • 11
  • 76
  • 121