Passing IEnumerable<string> as an attribute in an object for .NET 6 web API - asp.net-web-api

How do I send a POST request in swagger when my controller contains an object that has IEnumerable as an attribute in .NET 6 web API?
I got this error:
System.InvalidOperationException: Each parameter in the deserialization constructor on type 'EmailService.Message' must bind to an object property or field on deserialization. Each parameter name must match with a property or field on the object. The match can be case-insensitive.
This the post request that I've tried. I couldn't get it to work (application/json).
{
"to": [
"mail.com"
],
"subject": "Good day mate",
"content": "Have a cookie"
}
Post controller
[HttpPost]
public async Task<IActionResult> SendMail([FromBody] Message message)
{
await _emailSender.SendEmailAsync(message);
...
return Ok();
}
Message Model
public class Message
{
public List<MailboxAddress> To { get; set; }
public string Subject { get; set; }
public string Content { get; set; }
public Message(IEnumerable<string> to, string subject, string content)
{
To = new List<MailboxAddress>();
To.AddRange(to.Select(x => new MailboxAddress("emailAPI", x)));
Subject = subject;
Content = content;
}
}

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).

How to serialize a JSON property of type array to its proper .NET Array type rather than JArray

I have the following JSON to POST to my Web API endpoint.
[
{
"name": "REGION",
"value": ["MA", "SE", "SW"]
}
]
The Web API endpoint is as follows
public class Parameter
{
public string Name { get; set; }
// Value can be string, string[], int, or int[]
public dynamic Value { get; set; }
}
[Route("{chart}/data/")]
[HttpPost]
public IHttpActionResult GetData(string chart, IList<Parameter> parameters)
{
// ... do stuff ...
}
Whenever value in the JSON is an array the deserialized parameter's Value is a JArray rather than an array of string, int, etc. However, if value is a simply a string or number then Value in the deserialized parameters is also a string or a number.
What gives? Why isn't the array in the JSON being deserialized into an array of the proper type?
This was my solution. It "corrects" for JArray by inspecting the Value type after deserialization and converting it to the appropriate string[].
public class Parameter
{
public string Name { get; set; }
public dynamic Value { get; set; }
[OnDeserialized]
public void OnDeSerialized(StreamingContext context)
{
Type type = this.Value.GetType();
if (type == typeof(JArray))
{
var value = (this.Value as JArray).ToObject(typeof(string[]));
this.Value = value;
}
}
}

How to return 404 in case of action argument's type mismatch in web api?

I have a web api application and I have an http post action taking dto as following :
public class Account
{
public string Name { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
But using Postman I can pass it like this:
{"Name":"Simple Code",Email:"SimpleCode#gmail.com",Age:null}
When I send the request it sends Age as null.
How can I get my web api returning 404 without sending the request or am I forced to validate that inside my code?
Hi the most fast way for me is:
Mark as [Required] your DTO
public class Account
{
public string Name { get; set; }
[Required(AllowEmptyString = false)]
public string Email { get; set; }
[Required]
public int Age { get; set; }
}
then in your API method
public IHttpActionResult Post([FromBody] mydto){
// if model is not validated return 400 bad request
if(!ModelState.IsValid) return BadRequest(ModelState);
//or if is here it's ok
//return 200 OK
Ok(mydto);
}

the correct way to define web api post method

I have a .NET Web Api REST server which has a controller for the class customer which has a post method like this:
public HttpResponseMessage PostCustomer()
{
//getting the data in the request body****
return new HttpResponseMessage(HttpStatusCode.Created);
}
and this is my class:
class Customer
{
public Customer(string name, string tell, string pass, string add)
{
FnameLname = name;
Address = add;
Password = pass;
AccountNumber = tell;
}
public int CustomerId { get; set; }
public string AccountNumber { get; set; }
public string Password { get; set; }
public string Address { get; set; }
public string FnameLname { get; set; }
}
and i have a C# form application consumer which i'm using RESTSharp to do so.
i'm trying to do a post request to create a Customer but i had no luck setting it up.
this is what i have got so far:
{
Customer newc=new Customer(...);
var client = new RestClient("http://192.168.137.1:9090/");
var request = new RestRequest("api/Customer",Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddObject(newc);
var response = client.Execute(request);
}
but it doesn't work.
i don't know if my controller method is wrong or the request is wrong.
Your Post method needs to take in a Customer parameter so that the WebAPI will try to bind the JSON in the request body to the customer parameter
public HttpResponseMessage PostCustomer(Customer customer)
{ ... }

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

Resources