Web API Form - Url Encoded Complex Parameter - asp.net-web-api

Deserializing a complex type in WebAPI is giving me serious grief.
The data contains keys that are syntactically invalid in c# as property names.
How can I translate the key names?
Relevant: Web API form-urlencoded binding to different property names

You can use JSON.NET's JsonProperty to do the trick:
public class SomeModel {
[JsonProperty("YourCustomName")]
public string SomeProperty { get; set; }
}

I wrote my own url encoded object deserializer that made use of the JsonProperty attribute. Then I used it with a custom modelbinder.

Related

Can default ASP.Net WebApi parameter binding work for query parameters containing dash("-")?

I'm refering to parameter binding here:
http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
But the query parameter that I want to bind is 'api-version' which isn't a legal C# identifier. What is the simplest way to make this bind to a C# property called 'ApiVersion' [just as if the query parameter were actually 'apiVersion=']?
Use JSON.NET JsonProperty attribute magic,
[JsonProperty(PropertyName="api-version")]
public int ApiVersion { get; set; }
The FromUri attribute has an optional Name property.
public IHttpActionResult Version([FromUri(Name="api-version")] string apiVersion)
{
...
}

Why do we have to specify FromBody and FromUri?

Why are the FromBody and FromUri attributes needed in ASP.NET Web API`?
What are the differences between using the attributes and not using them?
When the ASP.NET Web API calls a method on a controller, it must set values for the parameters, a process called parameter binding.
By default, Web API uses the following rules to bind parameters:
If the parameter is a "simple" type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from a string.
For complex types, Web API tries to read the value from the message body, using a media-type formatter.
So, if you want to override the above default behaviour and force Web API to read a complex type from the URI, add the [FromUri] attribute to the parameter. To force Web API to read a simple type from the request body, add the [FromBody] attribute to the parameter.
So, to answer your question, the need of the [FromBody] and [FromUri] attributes in Web API is simply to override, if necessary, the default behaviour as described above. Note that you can use both attributes for a controller method, but only for different parameters, as demonstrated here.
There is a lot more information on the web if you google "web api parameter binding".
The default behavior is:
If the parameter is a primitive type (int, bool, double, ...), Web API tries to get the value from the URI of the HTTP request.
For complex types (your own object, for example: Person), Web API tries to read the value from the body of the HTTP request.
So, if you have:
a primitive type in the URI, or
a complex type in the body
...then you don't have to add any attributes (neither [FromBody] nor [FromUri]).
But, if you have a primitive type in the body, then you have to add [FromBody] in front of your primitive type parameter in your WebAPI controller method. (Because, by default, WebAPI is looking for primitive types in the URI of the HTTP request.)
Or, if you have a complex type in your URI, then you must add [FromUri]. (Because, by default, WebAPI is looking for complex types in the body of the HTTP request by default.)
Primitive types:
public class UsersController : ApiController
{
// api/users
public HttpResponseMessage Post([FromBody]int id)
{
}
// api/users/id
public HttpResponseMessage Post(int id)
{
}
}
Complex types:
public class UsersController : ApiController
{
// api/users
public HttpResponseMessage Post(User user)
{
}
// api/users/user
public HttpResponseMessage Post([FromUri]User user)
{
}
}
This works as long as you send only one parameter in your HTTP request. When sending multiple, you need to create a custom model which has all your parameters like this:
public class MyModel
{
public string MyProperty { get; set; }
public string MyProperty2 { get; set; }
}
[Route("search")]
[HttpPost]
public async Task<dynamic> Search([FromBody] MyModel model)
{
// model.MyProperty;
// model.MyProperty2;
}
From Microsoft's documentation for parameter binding in ASP.NET Web API:
When a parameter has [FromBody], Web API uses the Content-Type header
to select a formatter. In this example, the content type is
"application/json" and the request body is a raw JSON string (not a
JSON object). At most one parameter is allowed to read from the
message body.
This should work:
public HttpResponseMessage Post([FromBody] string name) { ... }
This will not work:
// Caution: This won't work!
public HttpResponseMessage Post([FromBody] int id, [FromBody] string name) { ... }
The reason for this rule is that the request body might be stored in a
non-buffered stream that can only be read once.
Just addition to above answers ..
[FromUri] can also be used to bind complex types from uri parameters instead of passing parameters from querystring
For Ex..
public class GeoPoint
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
[RoutePrefix("api/Values")]
public ValuesController : ApiController
{
[Route("{Latitude}/{Longitude}")]
public HttpResponseMessage Get([FromUri] GeoPoint location) { ... }
}
Can be called like:
http://localhost/api/values/47.678558/-122.130989
When a parameter has [FromBody], Web API uses the Content-Type header to select a formatter. In this example, the content type is "application/json" and the request body is a raw JSON string (not a JSON object).
At most one parameter is allowed to read from the message body. So this will not work:
// Caution: Will not work!
public HttpResponseMessage Post([FromBody] int id, [FromBody] string name) { ... }
The reason for this rule is that the request body might be stored in a non-buffered stream that can only be read once.
Please go through the website for more details:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api

Web API ModelBinders - how to bind one property of your object differently

I have the following action signature
[ValidateInput(false)]
public HttpResponseMessage PostParam(Param param)
With Param looking something like this:
public class Param {
public int Id { get; set;}
public string Name { get; set; }
public string Choices { get; set; }
}
Here's the hitch - what comes over the wire is something like this
{
Id: 2,
Name: "blah",
Choices: [
{
foo: "bar"
},
{
blah: "blo"
something: 123
}
]
}
I don't want "Choices" to deserialize - I want it stored as a string (yes, I understand the security implications). Understandably, I get an error because since the default binder does not know this.
Now with Asp Mvc creating a specific ModelBinder would be fairly simple. I'd
inherit DefaultModelBinder
override the property deserialization with my own
set the binder in my Application_Start using Binders.Add
Seems like with Web Api this is a different process - the System.Web.DefaultModelBinder doesn't have anything to override and that I can't hook things up using Binders.Add. I've tried looking around but couldn't find much on how to actually do what I want. This is further complicated since apparently the ModelBinders api changed quite a bit over Beta and RTM so there's a lot of outdated information out there.
In Web API you have to distinguish three concepts - ModelBinding, Formatters and ParameterBinding. That is quite confusing to people moving from/used to MVC, where we only talk about ModelBinding.
ModelBinding, contrary to MVC, is responsible only for pulling data out of URI. Formatters deal with reading the body, and ParameterBinding (HttpParameterBinding) encompasses both of the former concepts.
ParameterBinding is really only useful when you want to revolutionize the whole mechanism (i.e. allow two objects to be bound from body, implement MVC-style binding and so on) - for simpler tasks modifying binders (for URI specific data) or formatters (for body data) is almost always more than enough.
Anyway, to the point - what you want to achieve can very easily be done with a custom JSON.NET converter (JSON.NET is the default serialization library behind Web API JSON formatting engine).
All you need to do is:
public class Param
{
public int Id { get; set; }
public string Name { get; set; }
[JsonConverter(typeof(CustomArrayConverter))]
public string Choices { get; set; }
}
And then add the converter:
internal class CustomArrayConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
var array = JArray.Load(reader);
return JsonConvert.SerializeObject(array);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, JArray.Parse(value as string));
}
}
In this case we are telling JSON.NET in the converter to store Choices as string (in the read method), and when you return the Param object with the Choices property to the client (in the write method) we take the string and serialize to an array so that the output JSON looks identical to the input one.
You can test it now like this:
public Param PostParam(Param param)
{
return param;
}
And verify that the data coming in is like you wished, and the data coming out is identical to the original JSON.

Mapping Web API Incoming Form Field Names to my class?

I have written a Web API to be used as a webhook with a 3rd party service so they can send me data when an event occurs in their service. They have dictated what the posted data coming in will be. So I created a class for the incoming data such that each property name matches the field names coming from the posted form urlencoded data.
What I would LIKE to do is name things in my class in a more user-friendly manner than the names they are sending in their POST request. I cannot find if there is a way, maybe using attributes, that will tell the ModelBinder which form data to put into which property. Is there a way to do this?
Incidentally, when I thought they were sending JSON as the POSTED data, I was able to do this with JSON.NET and their JsonProperty attribute. But now that it's coming as application/x-www-form-urlencoded, I don't know how to do this same thing.
Thanks for your help!
please try something like this
[DataContract]
public class TestSerialization
{
[DataMember(Name = "field_one")]
public string ItemOne { get; set; }
[DataMember(Name = "field_two")]
public string ItemTwo { get; set; }
}

Web API parameters binding

I have this action in Web Api controller:
public Data GetData(ComplexModel model)
and model is
class ComplexModel
{
Guid Guid1 { get; set;}
Guid Guid2 { get; set;}
string String1 { get; set;}
}
i would like to specify custom binder for Guid type such as empty string or null would bind to empty guid and would like not use nullable type. Was trying to register model binder like this:
var pb = configuration.ParameterBindingRules;
pb.Insert(0, typeof(Guid), param => param.BindWithModelBinding(new GuidBinder()));
but it is not called and I am getting invalid model state with error message that empty string cant be converted to type Guid.
Remember, ParameterBindingRules checks the controller action parameter itself (ComplexModel in your case), not the contents of the parameter. You'd need to register a parameter binding against ComplexModel and do any processing with the custom binder to validate the model instance. Seems like you're better off making the Guid properties nullable despite your reluctance to do so.

Resources