Why isn't my POST JSON object getting serialized? I'm using Web API 2.0. My controller route looks like this:
[HttpPost]
[Route]
public async Task<IHttpActionResult> AddUserAsync([FromBody] User user)
{
//do some stuff
}
My User object looks like this:
public class User
{
Guid Id { get; set; }
string Name { get; set; }
}
When I pass the following JSON object the Id and Name props get serialized with null values:
{
"Id": "895C4492-B751-462C-9738-C6CB4E94E21F",
"Name": "Joe System"
}
Do I need to decorate User with [DataContract] or something like that?
How to manage this in Web API 2?
Your properties are not public. You need to make them public
public class User {
public Guid Id { get; set; }
public string Name { get; set; }
}
The model binder inspects the intended object type and populates public properties.
Reference Parameter Binding in ASP.NET Web API
Related
I am trying to get a controller to take json data from a post request and set how I wish them to be applied in the model. Example:
public string model1 {
public string name { get; set; }
public string value { get; set; }
}
public string model2 {
public string ident { get; set; }
public IEnumerable<model1> model { get; set; }
}
I want model1 to contain the key, value of the properties in the json object. For example { ident: "moo", model: { fish: "sticks" } } would produce model2 with ident: moo and ref model1 with an single entry containing name: fish and value: sticks.
I could easily do it in the controller, I am kind of wondering if there was a way via web api 2 that would allow me to specify how I wanted that model handled so I did not have to replicate it in every controller when a model ref model1.
Thanks
You may create a custom FilterAttribute and apply that new custom filter attribute globally within WebAPIConfig.cs file under Register Method
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new YourCustomFilterAttribute());
......
......
......
}
This custom filter attribute will run before every API-Method call.
hopefuly it will serve the core purpose.
I have the following C# ASP.Net Core Web API controller method for creating an "entity" using a POST:
[HttpPost("example")]
[SwaggerResponse(200,"Ok")]
public async Task<IActionResult> Create([FromForm]MyModel create)
{
return Ok();
}
The model is defined as this:
public class MyModel
{
public string PropA { get; set; }
public string PropB { get; set; }
public List<OtherProp> Other { get; set; }
}
public class OtherProp
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
When this is shown in swagger, you can see the "Other" property array looks like this:
Instead of the broken down model. How do I get swagger to breakdown this model?
Your are missing the Swagger Decorator Attributes, follow this below and replace the attributes with your specific Types / MyModel
Since you didn't put up your actual code, to see how it works, use the default samples for e.g. you can Install my Swashbuckle.Examples NuGet package. Decorate your methods with the new SwaggerResponseExample attributes below and you will see it work just fine!
// These attributes will help with your nested objects
[SwaggerResponse(HttpStatusCode.OK, Type=typeof(IEnumerable<Country>))]
[SwaggerResponseExample(HttpStatusCode.OK, typeof(CountryExamples))]
[SwaggerResponse(HttpStatusCode.BadRequest, Type = typeof(IEnumerable<ErrorResource>))]
public async Task<HttpResponseMessage> Get(string lang)
Also ensure you have it configured like so
configuration
.EnableSwagger(c =>
{
c.OperationFilter<ExamplesOperationFilter>();
})
.EnableSwaggerUi();
I try to make GET request via WebApi with complex object.
Request is like this:
[HttpGet("{param1}/{param2}")]
public async Task<IActionResult> GetRequest(string param1, int param2, [FromBody] CustomObject[] obj)
{
throw new NotImplementException();
}
Where CustomObject is:
[DataContract]
public class CustomeObject
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Email { get; set; }
}
How do I compose a valid GET request?
[FromBody] CustomObject[] obj ... GET request has no message body and thus you should change it to FromUri.
Sure, take a look at Documentation
public class GeoPoint
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
public ValuesController : ApiController
{
public HttpResponseMessage Get([FromUri] GeoPoint location) { ... }
}
Request would be like below, essentially you pass the entire object data as query string
http://localhost/api/values/?Latitude=47.678558&Longitude=-122.130989
An array of object example can be found in another post pass array of an object to webapi
If your complex object is defined by the server, you can model bind to it through the URI and dot notate the properties in the routing template. My advice is to keep this model to one level of properties. You can bind to more complex objects, but you'll quickly find yourself having to write your own model binder.
Note that your argument decorator will need to be changed to [FromUri] to bind a complex object through the Uri. Servers are not required to support GET bodies and most do not.
public class CustomObject
{
public string Name { get; set; }
public string Email { get; set; }
}
[HttpGet]
[Route("{foo.Name}/{foo.Email}")]
public HttpResponseMessage Get([FromUri]CustomObject foo)
{
//...body
return Request.CreateResponse(HttpStatus.OK, foo);
}
You can pass it as a stringified json or use the request body via post instead of get.
Code:
[HttpPost("user/register")]
public IActionResult Register([FromBody] User user)
{
if (user?.Name is null || user?.Password is null)
{
return BadRequest(new {message = "User is null and/or name and password are not provided"});
}
else
{
// Add to db
return Json(user);
}
}
Also the User class:
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Password { get; set; }
public string Role { get; set; }
}
It should basically get a user and add it to the database. I tried sending this json:
{ "Name": "Batman", "Password": "IronmanSucks"}
The app caught the request, but the user object was null. I also tried with:
{ "user": { "Name": "Batman", "Password": "IronmanSucks"} }
But according to this documentation, the first json should have worked fine.
Here is a link to an example http request in postman
Does this have to do with the headers or is it a bug in .NET Core 2.0?
This can only happen if the type does not have a parameterless constructor, thus this can be simply fixed by adding such.
I believe that the Model is coming up as invalid hence why it is null.
You should try adding a [BindNever] attribute into the User class for the Role and Guid properties, seeing as you aren't using them.
If that doesn't work you may try using extended classes like so:
public class User
{
public string Name { get; set; }
public string Password { get; set; }
}
public class DataUser : User
{
public Guid Id { get; set }
public string Role { get; set; }
}
If you're using MVC Core instead of MVC, make sure you add Json Formaters (from Microsoft.AspNetCore.Mvc.Formatters.Json). In your Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvcCore()
.AddJsonFormatters();
}
This should help the [FromBody] to de-serialize your Post content
I would like to bind submission of JSON like this
{
"id": 1,
"name": "bob",
"phone": "(425) 882-8080"
}
to...
class Model
{
public int Id { get; set; }
public string Name { get; set; }
public PhoneNumber Phone { get; set; }
}
where the PhoneNumber class is able to bind to the phone string in the JSON.
The idea was the use a json.net custom converter like:
class Model
{
public int Id { get; set; }
public string Name { get; set; }
[JsonConverter(typeof(PhoneNumberCoverter))]
public PhoneNumber Phone { get; set; }
}
The problem is that MVC is not even trying to use the ReadJson method. Model.Phone == null.
I have tried a few things. Hoping that if I had implicit operative overrides to and from string for the PhoneNumber class, it may just do it automatically. Nope.
What is the correct way to customize model binding for this scenario?
I think you expect that when your action method like
public ActionResult Post(Model myModel)
{
// you expect that myModel should has used JSonConverter and provide you data.
}
Above thing will not work as you expected and reason for this JsonConvertor attribute is specific Json.net and not MVC attribute so DefaultModelBinder will not consider that attribute to bind your data.
Possible correct and simple way to do (But not strongly type)
public ActionResult Post(string jsonData)
{
// Now here use Json.net JsonConvert to deserialize string to object of Type Model.
}
Another solution to is to build your own custom modelbinder and use Json.net inside that.