the correct way to define web api post method - asp.net-web-api

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

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 send array of custom object as a multiform data content to asp.net web api via postman?

How to properly send array of custom objects to asp.net web api via Postman? What I have done is:
Custom class:
public class SystemConsumerConfiguration
{
public int SystemId { get; }
public Uri CallbackUrl { get; }
public SystemConsumerConfiguration()
{
}
public SystemConsumerConfiguration(int systemId, Uri callbackUrl)
{
SystemId = systemId;
CallbackUrl = callbackUrl;
}
}
View model in REST API:
public class PostDigitalPostSubscriptionConfiguration
{
public IReadOnlyList<SystemConsumerConfiguration> SystemsModel { get; set; }
public IFormFile Certificate { get; set; }
public PostDigitalPostSubscriptionConfiguration()
{
}
}
And now, I make a request in Postman
The problem is, that model is bound with default values:
Forgot to have public setters in SystemConsumerConfiguration.
Should be like this:
public class SystemConsumerConfiguration
{
public int SystemId { get; set; }
public Uri CallbackUrl { get; set; }
public SystemConsumerConfiguration()
{
}
public SystemConsumerConfiguration(int systemId, Uri callbackUrl)
{
SystemId = systemId;
CallbackUrl = callbackUrl;
}
}
Answered in:
Default value in an asp.net mvc view model

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);
}

How to Post to WebApi using HttpClient?

I am trying to post to a WebAPI using HttpClient using an authentication token.
However I am always getting default values on the WebAPI method not the actual values I am sending.
This is my code:
C# Console APP:
public static async Task<string> Rent(HttpClient client, string token, int idCommunityAmenity, int idHome, DateTime startDate, DateTime endDate)
{
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:50634/api/amenity/RentCommunityAmenity");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
var postContent = new
{
idCommunityAmenity = idCommunityAmenity,
idHome = idHome,
startDate = startDate,
endDate = endDate
};
request.Content = new StringContent( JsonConvert.SerializeObject(postContent), Encoding.UTF8, "application/json");
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
WebAPI
[HttpPost("RentCommunityAmenity")]
public async Task<JsonResult> Post([FromBody]int idCommunityAmenity, [FromBody]int idHome, [FromBody]DateTime startDate, [FromBody]DateTime endDate)
{
var communityAmenity = new AmenityReservation
{
IdCommunityAmenity = idCommunityAmenity,
StartDate = startDate,
EndDate = endDate,
IdHome = idHome
};
_context.AmenityReservation.Add(communityAmenity);
await _context.SaveChangesAsync();
return new JsonResult(true);
}
My guess is that the content is not being set up correctly, because when I inspect it I don't see the the json string.
When I hit the post method I get: idCommunityAmenity = 0, idHome=0,...
Thanks for the help.
create a model for the data you pass to the webapi endpoint.
add all the validation to it.
something like :
[DataContract]
public sealed Class BookingModel
{
[Required]
[DataMember]
public int IdCommunityAmenity { get; set; }
[DataMember]
public DateTime StartDate { get;set;}
[DataMember]
public DateTime EndDate { get; set; }
[Required]
[DataMember]
public int IdHome { get; set;}
}
Use whatever other validation you need on the model. DataContract and DataMember comes from System.ComponentModel.DataAnnotations which you add as a reference separately. Sometimes, depending on how your project is setup, your api will not receive data from your post because the property members don't serialize. Making sure you have those can actually help a lot.
Now in webapi you can check your model is valid like this:
[HttpPost("RentCommunityAmenity")]
public async Task<JsonResult> Post([FromBody] BookingModel)
{
if ( !ModelState.IsValid )
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
//your code here.
}
This is the way I fix it.
I took the reference from this answer
Basically you have to receive an object on the WebAPI side.
Like this:
[HttpPost("RentCommunityAmenity")]
public JsonResult Post([FromBody]MyModel value)
{
}
public class MyModel
{
public int idCommunityAmenity { get; set; }
public int idHome { get; set; }
public DateTime startDate { get; set; }
public DateTime endDate { get; set; }
}

Is there something like [Bind(Exclude="Property")] for asp.net web api?

I'm trying to exclude a property from my Post Action in a web api controller, is there something like [Bind(Exclude="Property")] for asp.net web api?
This is my model:
public class ItemModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
I want to exclude the Id in the Post Action, because it is autogenerated, but I need to return it in my Get Action.
I Know I could have two models, one for my Post action and one for my Get action, but I'm trying to do this with just one model.
I would favour mapping models but this could be achieved by checking if the request is a POST in a ShouldSerialize method:
public class MyModel
{
public string MyProperty1 { get; set; }
public string MyProperty2 { get; set; }
public bool ShouldSerializeMyProperty2()
{
var request = HttpContext.Current.Request;
if (request.RequestType == "POST") return false;
return true;
}
}
Where your method name is the name of the property prefixed with ShouldSerialize.
Note this will work for JSON. For XML, you will need to add the following line to your config:
config.Formatters.XmlFormatter.UseXmlSerializer = true;
You can simply create a DTO for POST.

Resources