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
Post a Comment