WebApi action losing child collection from posted json - asp.net-web-api

Suppose I post an object like this
{"Dto" : {
"DtoId" : 1,
"DtoThing" : "Some value",
"DtoChildStuff" : [{"CsId" : 1, "ChildProperty" : "SomeThing"}]
}}
to a WebApi action like this
[HttpPost]
public Response<MyDto> Post(DtoWrapper<MyDto> input)...
where the parameter is just some object with a property MyDto of type MyDto, and MyDto is like this
[DataContract]
public class MyDto
{
[DataMember]
public int DtoId {get;set;}
[DataMember]
public string DtoThing {get;set;}
[DataMember]
public List<ChildStuffDto> DtoChildStuff {get;set;}
}
[DataContract]
public class ChildStuffDto
{
[DataMember]
public int CsId {get;set;}
[DataMember]
public string ChildProperty {get;set;}
}
and (by the way) the DtoWrapper is just
public class DtoWrapper<T>
{
public T Dto {get;set;}
// So that I can add some other info that I need //
}
Why can't the action see any child objects. If I change the type on the parameter to object, I can see the child object being posted in, but it doesn't get deserialised. Any ideas?

Ok here's my answer, but I'd love to know if there's a way I can get it to happen without adding this line of code to my action.
public Response<MyDto> Post(object input)
{
dynamic myWrapperObj = JsonConvert.DeserializeObject<SomeWrapperForMyDto>
(input.ToString());
...
If that's how it has to be then fair play, but it seems a shame. Thanks for any posts :)

Related

Asp.net core 2.2 web api FromForm of complex object contains only null values

Hi I am trying to build an endpoint for slack commands in asp.net core 2.2.
I have a data structure representing a commandrequest from slack like so:
public class SlackCommandDTO
{
[FromForm(Name = "token")]
public string Token { get; set; }
[FromForm(Name = "team_id")]
public string TeamId { get; set; }
[FromForm(Name = "team_domain")]
public string TeamDomain { get; set; }
[FromForm(Name = "channel_id")]
public string ChannelId { get; set; }
[FromForm(Name = "channel_name")]
public string ChannelName { get; set; }
[FromForm(Name = "user_id")]
public string UserId { get; set; }
[FromForm(Name = "user_name")]
public string UserName { get; set; }
[FromForm(Name = "command")]
public string Command { get; set; }
[FromForm(Name = "text")]
public string Text { get; set; }
[FromForm(Name = "response_url")]
public string ResponseUrl { get; set; }
[FromForm(Name = "trigger_id")]
public string TriggerId { get; set; }
}
My controller to receive data looks like this:
[Route("api/[controller]")]
[ApiController]
public class CustomerServiceController : ControllerBase
{
// POST api/customerservice
[HttpPost]
public void Post([FromForm] SlackCommandDTO command)
{
Console.Write(command.Token);
}
}
my startup.cs looks like this
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
}
I have tried setting the compatability settings in startup.cs to 2.1 and 2.2.
The result is always an instance of the object that contain null in all properties.
I have tried setting the decorator to [FromBody] (not that that is supposed to work) but in that case I get 415 unsupported media type (as it should).
I have tried sending the requests with content-type x-www-form-urlencoded and form-data as well as text/plain and application/json. the latter two return 415.
I have also tried sending the request through swagger with the same result and curl both using -d keyword and -F keyword for each pair of data.
If I am missing some information please let me know, I am drawing a blank here on how to solve it so please help.
The data I am receiving is from Slack according to this article about implementing slash commands in slack.
https://api.slack.com/slash-commands#responding_to_commands
I have solved my problem.
My issue was the fundamental misunderstanding that the parameters would be bound as a single object when using the FromForm attribute when actually I was supposed to parameterize each field as a string input in the post method like so:
[Route("api/[controller]")]
[ApiController]
public class CustomerServiceController : ControllerBase
{
// POST api/customerservice
[HttpPost]
public void Post([FromForm] string token,
[FromForm] string team_id,
[FromForm] string team_domain,
[FromForm] string channel_id,
[FromForm] string channel_name,
[FromForm] string user_id,
[FromForm] string user_name,
[FromForm] string command,
[FromForm] string text,
[FromForm] string response_url,
[FromForm] string trigger_id)
{
Console.Write(token);
}
}
[FromForm] is not for annotating properties on your model. It's for indicating how an action param will be bound. If you were accepting JSON, you could achieve this via [JsonProperty], but there's no way to change the property names for binding from form. They need to match, i.e. you'll either need to change your properties to stuff like Team_Id (with the underscore) or change your field names to stuff like teamId (without the underscore).

ASP.NET Web API deep model binding

I've noticed (even in Web API 2.1) that deep parameter types get filled (processed by the model binder) only on the first level. That is :
public class Person
{
public string Name { get; set; }
public PersonDetails Details { get; set; }
}
public class PersonDetails
{
public string Address { get; set; }
public int Age { get; set; }
}
// ...
public class PersonController : ApiController
{
[HttpPost]
public void ProcessPerson(Person person)
{
// person.Name is filled in correctly
// person.Details.Address and person.Details.Age are not filled in correctly. That is, they have default values (null and 0)
}
}
Is there a simple solution for this problem, except flatting out the Person class like so ?
public class PersonData
{
public string Name { get; set; }
public string Address { get; set; }
public int Age { get; set; }
}
Later edit 1 :
If I flatten the Person class I get all the data correctly
The request is made by POST (and not GET) because I need to ensure there is no caching and since the operation alters state it would be semantically incorrect to use GET

Need example for web api post with multiple parameter

In c#, Need example for web api post with multiple parameter ,Below i have attached my sample code.Please look into the InsertLeave method.In my code without CompanyId parameters working fine. When i add the companyid not able to invoke.
namespace AeS.SaaSAPI_2116
{
[RoutePrefix("{CompanyId}/{SecurityKey}")]
public class LeaveController : ApiController
{
[HttpPost]
[Route("{APIName}/x")]
public string InsertLeave(List<LeaveRequest> objList, string CompanyId)
{
foreach (LeaveRequest LR in objList)
{
}
return "Sucess ";
}
}
}
public class LeaveRequest
{
[Required]
public string EMP_STAFFID { get; set; }
[Required]
public string LEAVE_TYPE { get; set; }
}
}
I think you can use send your parameter to the server by request body, You will have to create a single class that wrapping your all parameters.You can use model binding to resolve this kind of issue

MVC3 Optional parameters

This is a follow up to this:
What does MVC3 do with C# Optional Parameters?
I have an action with the following signature:
public ViewResult Show(int Id, PublishingErrorSummary pubErrors=null, String title=null)
On requesting server/show/1 pubErrors is not null, but title is null. How is it possible? These are just two objects but string somehow manages to become null. Where can I fix this?
Edit: class definition added
public class PublishingErrorSummary
{
public List<string> StepOneErrors { get; set; }
public List<string> StepTwoErrors { get; set; }
public List<string> StepThreeErrors { get; set; }
public List<string> StepFourErrors { get; set; }
}
PublishingErrorSummary is a complex object. The default model binder always initializes complex objects. It doesn't really make sense to set its default value to null. Same stands for the title parameter. Strings are reference types and their default value will be null anyway if no request parameter title is sent.

How to update a property of an abstract with an inheriting/using a subblass in MVC

I have an abstract class
public abstract class Member
{
public string ID { get; set; }
public string Username { get; set; }
public int MemberType { get; set; }
public abstract string MemberName { get; set; }
public int Status { get; set; }
}
public class Person : Member
{
public string LastName { get; set; }
public string FirstName{ get; set; }
}
public class Business : Member
{
public string BusinessName { get; set; }
public string TaxNo { get; set; }
}
The class was mapped using fluent API,
Now, is there a way to update the "Status" property from the view(having Member model) without using or going to a subclass (Person/Business)?
I just tried it, but it says "Cannot create an abstract class.", when submitting the page.
Or there is a correct way to do this?
Thanks
Not in EF. You have to instantiate an object to work with EF, and you can't instantiate an abstract class.
You could make the class not be abstract. Or you could use a stored proc to update the field, or some direct sql.
It sounds like your problem is that your action method has an abstract type as a parameter, which the default model binder can't do anything with. If you are dead set on using the same view for two different classes, you may need to implement your own model binder to inspect in the incoming request and decide which type, Person or Business, to instantiate.
Check out this link for more information on creating a custom model binder:
http://odetocode.com/blogs/scott/archive/2009/05/05/iterating-on-an-asp-net-mvc-model-binder.aspx
This seems like a similar problem to the one I've answered previously here:
ASP.NET MVC 3: DefaultModelBinder with inheritance/polymorphism

Resources