Pass parameters to HttpPost method of controller - c#

There something very basic i must be missing which i cant understand what it is.
I have a method at the controller
[Route("SomeMethod")]
[HttpPost]
public IActionResult SomeMethod([FromBody]int interval)
{
...
}
And im seing with postman post request {"interval": 2000 }, The interval at the controller is not initialized. If i change the method paramater to be object i get ValueKind = Object : "{"interval": 2000 }"
I also tried the parameter to be string and then the body i sent is {"interval": "2000" }, And at the controller i get null
Based on this question i tried:
[Route("SomeMethod")]
[HttpPost]
public IActionResult SomeMethod([FromBody]JsonElement interval)
{
...
}
And got ValueKind = Undefined : "" At the contoroller, And also:
services.AddControllers()
.AddNewtonsoftJson();
When the method expects an int and got 0

According to documentation you have to pass it in request body as it is, just a value, without property name. But its imposible to bind more then one parameter. Anyway, I would reccomend to use proper object to bind body parameters.
[Route("SomeMethod")]
[HttpPost]
public IActionResult SomeMethod([FromBody]Request request)
{
return Ok();
}
public class Request
{
public int Interval { get; set; }
}

Related

Can't bind json in POST request body to class argument in .net-core

I'm trying to make a post request from my Angular frontend to the .net Core 3.1 backend, but in the controller method the argument object only gets by default 0 and null values;
let response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(obj)
});
[ApiController]
[Route("[controller]")]
[ApiExplorerSettings(IgnoreApi = true)]
public class RequestController : ControllerBase
{
[HttpPost]
public async Task<IAResponseViewModel> PostAnswer([FromBody] IAResponseViewModel something)
{
var temp = something.ToString();
if (!ModelState.IsValid)
{
Console.WriteLine();
}
return something;
}
}
public class IAResponseViewModel
{
public string AdministrationId { get; }
public int RoundUsesItemId { get; }
public int ResponseOptionId { get; }
}
The JSON object I see being submitted
{AdministrationId: "12345678-00e8-4edb-898b-03ee7ff517bf", RoundUsesItemId: 527, ResponseOptionId: 41}
When inspecting the controller method the 3 values of the IAResponseViewModel are null or 0
When I change the argument to object I get something with a value of
ValueKind = Object : "{"AdministrationId":"12345678-00e8-4edb-898b-03ee7ff517bf","RoundUsesItemId":523,"ResponseOptionId":35}"
I've tried with and without the [FromBody] attribute, changing the casing of the properties of the controller method's argument and the frontend argument, copy pasting the viewmodel attributes on to the submitted object keys, wrapping the posted object in a 'something' object. the ModelState.IsValid attribute shows as true.
I've red other answers such as Asp.net core MVC post parameter always null &
https://github.com/dotnet/aspnetcore/issues/2202 and others but couldn't find an answer that helped.
Why isn't the model binding working and how do I populate the viewmodel class I'm using with the json data?
From a comment on my original question:
Could it be because the properties in IAResponseViewModel are 'get only'?
Indeed this was the problem. Giving the properties (default) set methods fixed the problem.

How to pass null in body to endpoint within asp.net core 3.1

I have the following action within an asp.net core 3.1 controller
[ApiController]
[Route("example")]
public class MyExampleController : ControllerBase
{
[HttpPost("{id}/value")]
public async Task<IActionResult> Post(string id, [FromBody] int? value)
=> Task.FromResult(Ok());
}
This works fine if I post a body value of int (for example: 1, 2, etc...)
However, I can't find a way to get a null value passed in.
If I pass in an empty body or null body I get a status code of 400 returned with a validation message of A non-empty request body is required. returned.
I've also tried to change the value parameter to be an optional argument with a default value of null:
public async Task<IActionResult> Post(string id, [FromBody] int? value = null)
How do I pass in null to this action?
Finally figured this out, big thanks to #Nkosi and #KirkLarkin helping fault find this.
Within the Startup.cs when configuring the controllers into the container we just need to alter the default mvc options to AllowEmptyInputInBodyModelBinding
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(x => x.AllowEmptyInputInBodyModelBinding = true);
}
This way we can pass in null into the body of the post and it works perfectly fine. It also still applies the normal model validation via the attributes without having to check the ModelState manually:
public async Task<IActionResult> Post(string id,
[FromBody][Range(1, int.MaxValue, ErrorMessage = "Please enter a value bigger than 1")]
int? value = null)
Reference Automatic HTTP 400 responses
The [ApiController] attribute makes model validation errors automatically trigger an HTTP 400 response
This would explain the returned response.
Remove the [ApiController] to allow the invalid request to still make it to the controller action and also if the additional features of having that attribute is not critical to the current controller.
It would however require that the desired featured be applied manually
[Route("example")]
public class MyExampleController : ControllerBase {
[HttpPost("{id}/value")]
public async Task<IActionResult> Post(string id, [FromBody] int? value) {
if (!ModelState.IsValid) {
//...
return BadRequest(ModelState);
}
//...
return Ok();
}
}
It seems it has something to do with the way a single value is conducted through JSON. It expects a value and the null just simply creates an empty request body. You should consider defining a class like this
public class MyInt{
public int Value { get; set; }
public bool IsNull { get; set; }
}
[ApiController]
[Route("example")]
public class MyExampleController : ControllerBase
{
[HttpPost("{id}/value")]
public IActionResult Post(string id, [FromBody]MyInt value)
{
if(value.IsNull){
}
else{
}
return Ok();
}
}
In other words when you POST you post not just use a default value. You could do that the other way like this
[HttpPost("{id}/value")]
public IActionResult Post(string id, [FromBody]int value)...
[HttpGet("{id}/value")]
public IActionResult Get(string id)...//use the default value here

Method Overloading With Objects

I tried to use method overloading in C# WebApi
[HttpPost]
public HttpResponseMessage UpdateUser(PersonalInfoModel personalInfo){}
[HttpPost]
public HttpResponseMessage UpdateUser(RolesModel roles){}
On the client I call:
/UpdateUser(json)
let say we want to update the user personal info, so the json will look like:
{firstName: "testUser", lastName: "testUser", age: 20}
and it should match the PersonalInfoModel that looks like this:
public class PersonalInfoModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
My idea was to update different parts of the user information and to keep
it clear, meaning call the same method with a different object and I thought
UpdateUser on the server will know what function to use and it failed
because the server didn't know what route to use.
Next thing I tried to do is to call only One route in the server like this:
[HttpPost]
public HttpResponseMessage UpdateUser(dynamic obj){
return Update(obj);
}
public Update(PersonalInfoModel personalInfo){}
public Update(RolesModel roles){}
The above also didn't work with the error:
"The best overloaded method match for has some invalid arguments"
I tried to look on google but with no success to overcome this.
Try this
[HttpPost]
[Route("/api/[controller]/personalInfo")]
public HttpResponseMessage UpdateUser(PersonalInfoModel personalInfo){}
[HttpPost]
[Route("/api/[controller]/roles")]
public HttpResponseMessage UpdateUser(RolesModel roles){}
You need to use Route Attribute to differentiate between these methods for client to call.
[Route("api/Update")]
public class UpdateController
{
[Route("PersonalInfo")]
[HttpPost]
public HttpResponseMessage UpdateUser(PersonalInfoModel
personalInfo){}
[Route("Roles")]
[HttpPost]
public HttpResponseMessage UpdateUser(RolesModel roles){}
}
Then client calls api/Update/PersonalInfo to call first method and
api/Update/Roles to call second method
Hope this helps.

How to fix - The requested resource does not support http method 'POST'

Below is WebAPI action. On googling about the below error:-
The requested resource does not support http method 'POST'
I got number of links & updated my api accordingly but still I am getting the same error.
Web api not supporting POST method
ASP.NET Web Api: The requested resource does not support http method 'GET'
[AcceptVerbs("POST")]
[HttpPost]
[Route("rename/{userId}/{type}/{title}/")]
public IHttpActionResult Rename([FromBody] int userId, [FromBody] string type, [FromBody] string title)
{
//my api stuff
}
But still when calling the above via post man throws the error.
How do I get rid of this error??
Also is it possible to fix this without using [FromBody] attribute in the method parameters list?
Any help/suggestion highly appreciated.
Thanks.
You have declared route which requires url parameters
[Route("rename/{userId}/{type}/{title}/")]
So when you send request to api/customer/rename it does not match this method. You should remove parameters which you are passing in request body from route parameters
[Route("rename")]
Make sure that you have appropriate RoutePrefix("api/customer") attribute on your controller.
Second problem is multiple [FromBody] parameters. You will get can't bind multiple parameters error. There is limitation - you can mark only one parameter as FromBody. See Sending Simple Types notes:
Web API reads the request body at most once, so only one parameter of
an action can come from the request body. If you need to get multiple
values from the request body, define a complex type.
You should create complex type which will hold all parameters
public class RenameModel
{
public int UserId { get; set; }
public string Type { get; set; }
public string Title { get; set; }
}
And change method signature to
[HttpPost]
[Route("rename")]
public IHttpActionResult Rename(RenameModel model)
And send request data as application/x-www-form-urlencoded
[Route("rename/{userId}/{type}/{title}/")]
public IHttpActionResult Rename([FromBody] int userId, [FromBody] string type, [FromBody] string title)
The last answer is correct, you're asking for these parameters in the route, but saying that you expect them in the post body. Also, usually the route would begin with a noun rather than a verb. What is it you're renaming? (i.e. [Route("users/rename/{userId}/{type}/{title}")]
Based on your initial post, try this instead:
[HttpPost]
[Route("rename/{userId}/{type}/{title}" Name = "RenameUser"]
public IHttpActionResult Rename(int userId, string type, string title)
{
_myServiceMethod.Rename(userId, type, title);
return new StatusCodeResult(HttpStatusCode.Created, this);
}
Or, if you wanted to do a post with the info in the body:
Declare your data contract:
public class User
{
public string Type { get; set; }
public string Title { get; set; }
}
Then on the endpoint:
[HttpPost]
[Route("rename/{userId}", Name = "RenameUserPost")]
public IHttpActionResult RenameUserPost(int userId, [FromBody] User userData)
{
return new StatusCodeResult(HttpStatusCode.Created, this);
}
Note that in both returns 'this' refers to your controller class that inherits from ApiController. Verified both of these in swagger, and they accept POSTs and return status codes.
Hope this helps.
I had this error for wrong string in Route string on top of my action.
[Route("api/TestReaderPercentStudyHomework/AddOrUpdate")]

webapi httppost sending the parameter as object

I have a webapi method as httppost as shown below. I am trying to make a request using
fiddler but I cant get param object. It is null if I send the request as shown in the image. what am I doing wrong?
[ActionName("getCustomerByName")]
[HttpPost]
public async Task<List<Customer>> GetcustomerByName(object param)
{
}
What do you expect object param to be?
Does the request body JSON string represent a Customer ?
If yes, use Customer as the type instead of object eg
public async Task<List<Customer>> GetCustomerByName(Customer param)
If no then define a class (Any name) with the same field names as the JSON string you are passing and use that class instead of object eg
public class QueryArgs
{
public int Id { get; set; }
// rest of your fields go here
}
public async Task<List<Customer>> GetCustomerByName(QueryArgs param)

Categories

Resources