c# - Attribute Routing Values into Model/FromBody Parameter -


when using attribute routing in web api (2), able route parameters url model parameter automatically. reason validation performed in filter before reaches action, , without additional information not available.

consider following simplified example:

public class updateproductmodel {     public int productid { get; set; }     public string name { get; set; } }  public class productscontroller : apicontroller  {     [httppost, route("/api/products/{productid:int}")]     public void updateproduct(int productid, updateproductmodel model)      {          // model.productid should == productid, default (0)     } } 

sample code post this:

$.ajax({     url: '/api/products/5',     type: 'post',     data: {         name: 'new name'   // nb: no productid in data     } }); 

i want productid field in model populated route parameters before entering action method (i.e. available validators).

i'm not sure part of model binding process need try , override here - think it's bit handle [frombody] part (which model parameter in example).

it not acceptable set within action (e.g. model.productid = productid) need have been set before reaches action.

referencing article parameter binding in asp.net web api

model binders

a more flexible option type converter create custom model binder. model binder, have access things http request, action description, , raw values route data.

to create model binder, implement imodelbinder interface

here model binder updateproductmodel objects try extract route values , hydrate model matching properties found.

public class updateproductmodelbinder : imodelbinder {      public bool bindmodel(system.web.http.controllers.httpactioncontext actioncontext, modelbindingcontext bindingcontext) {         if (!typeof(updateproductmodel).isassignablefrom(bindingcontext.modeltype)) {             return false;         }          //get content of body , convert model         object model = null;          if (actioncontext.request.content != null)             model = actioncontext.request.content.readasasync(bindingcontext.modeltype).result;          model = model ?? bindingcontext.model             ?? activator.createinstance(bindingcontext.modeltype);          // check values provided in route or query string          // matching properties , set them on model.          // note: override existing value set.         foreach (var property in bindingcontext.propertymetadata) {             var valueprovider = bindingcontext.valueprovider.getvalue(property.key);             if (valueprovider != null) {                 var value = valueprovider.convertto(property.value.modeltype);                 var pinfo = bindingcontext.modeltype.getproperty(property.key);                 pinfo.setvalue(model, value, new object[] { });             }         }          bindingcontext.model = model;          return true;     } } 

setting model binder

there several ways set model binder. first, can add [modelbinder] attribute parameter.

public httpresponsemessage updateproduct(int productid, [modelbinder(typeof(updateproductmodelbinder))] updateproductmodel model) 

you can add [modelbinder] attribute type. web api use specified model binder parameters of type.

[modelbinder(typeof(updateproductmodelbinder))] public class updateproductmodel {     public int productid { get; set; }     public string name { get; set; } } 

given following simplified example above model , modelbinder

public class productscontroller : apicontroller {     [httppost, route("api/products/{productid:int}")]     public ihttpactionresult updateproduct(int productid, updateproductmodel model) {         if (model == null) return notfound();         if (model.productid != productid) return notfound();          return ok();     } } 

the following integration test used confirm required functionality

[testclass] public class attributeroutingvaluestests {     [testmethod]     public async task attribute_routing_values_in_url_should_bind_parameter_frombody() {         var config = new httpconfiguration();         config.maphttpattributeroutes();          using (var server = new httptestserver(config)) {              var client = server.createclient();              string url = "http://localhost/api/products/5";             var data = new updateproductmodel {                 name = "new name" // nb: no productid in data             };             using (var response = await client.postasjsonasync(url, data)) {                 assert.areequal(httpstatuscode.ok, response.statuscode);             }         }     } } 

Comments

Popular posts from this blog

java - Suppress Jboss version details from HTTP error response -

gridview - Yii2 DataPorivider $totalSum for a column -

Sass watch command compiles .scss files before full sftp upload -