How can I POST form data to ASP.NET WebAPI 2? - c#

I'm having trouble accessing parameters submitted via POST in my ASP.NET WebAPI 2 application. I hav a very trivial controller and an action, which simply captures the parameter and returns 200.
public IHttpActionResult Post([FromBody] string foo)
{
return Ok();
}
And I'm making the request via cURL
curl http://localhost:24196/api/checkpoints -XPOST -d foo=bar
The parameter ends up being null. The same happens when I try this with jQuery.
$.post("http://localhost:24196/api/checkpoints", { foo: "bar" })
I've found a post that seems to describe this issue, but I'm not sure if that's really the correct way to fix this (using the = encoding).
What is the correct/standard way of talking to a WebAPI service, and how does the parameter binding actually work in these cases?

As documented in the link, FromBody expects data in a certain way =foo - re: there is no "key". So:
Curl:
curl -X POST -d "=bar" http://localhost/controller
Jquery:
var _data = { "": "bar" };
Update:
Reference for this behavior: Sending simple types
By default, Web API tries to get simple types from the request URI. The FromBody attribute tells Web API to read the value from the request body.
...If you need to get multiple values from the request body, define a complex type.
Second, the client needs to send the value with the following format:
=value
Hth...

Related

How to use multiple parameters when sending data as application/json in controller

I have the following question:
Currently, I do have an ASP.Net Core Application set up with some generic code in the frontend, which sends all POST requests with a JSON payload and with application/json set as content type.
My controllers accept these calls like this for example:
public async Task<JsonResponse> AddToPortfolio([FromBody] AddPortfolioUiModel addPortfolioUiModel)
{
...
}
So, when my frontend code sends a POST for this controller action, it will properly read the parameter UiModel from the JSON.
Good enough.
However, sometimes I do have controller calls which might only require receiving a single string. As of now, I would have to wrap this in a model and tag it as [FromBody]. This is fairly annoying to do, and I was wondering if there is no easier way to just accept it like this instead:
public async Task<JsonResponse> ControllerMethod(string myParam)
{
...
}
Is this easily possible while still keeping the application/json content type attached to all requests coming from my JS client? I don't really want to juggle around with multiple content types, so this would be great.

Failed to load resource: the server responded with a status of 404 ()

I am writing a REST API in .net core. I am trying to test the API using Postman and I am getting an error saying
Failed to load resource: the server responded with a status of 404 ()
I know this error occurs when the route does not match. Not sure, what am I doing wrong with the route. Below is my code with the Route at the top:
namespace RecLoad.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class RecLoadPrimeController : ControllerBase
{
[Route("RecLoadPrime/insertRecLoadData/{RecStartDate}/{RecEndDate}")]
[HttpPost]
public void insertRecLoadData(string RecStartDate, string RecEndDate)
{
RecLoadDataProvider dataProvider = new RecLoadDataProvider();
dataProvider.InsertCardsData(RecStartDate, RecEndDate);
}
}
}
The URL that I am trying to test in Postman is below:
https://localhost:44360/api/RecLoadPrime/insertRecLoadData/?RecStartDate=01/01/2020&RecEndDate=01/02/2020
I am very new to API, this is the first API that I am writing. Below is the image for application structure. Its extremely simple:
Any help will be greatly appreciated.
A 404 error means not found. This means Postman cant find the end point you are trying to hit.
Your [Route] attribute needs to be updated. The root of this endpoint (controller) it's RecLoadPrime. So get rid of that part. If you are just trying to test, update it to [Route("insert")].
Using ? in your URL means you are passing query parameters. Which are usually used on GET requests not on POST requests.
Web API expects you to use Model Binding for passing in parameters. Meaning map the post parameters to a strongly typed .NET object, not to single parameters. Alternatively, you can also accept a FormDataCollection parameter on your API method to get a name value collection of all POSTed values.
For example: Create a small class called Card, with the properties startDate, and endDate. Make them DateTime. Now use that in the method signature public void insertRecLoadData([FromBody]Card card)
In Postman, you are now going to use the Body option and create a JSON representation of this new class we created.
For example: { "startDate": "2020-03-23", "endDate": "2020-03-27" }
In the route, you are going to use: POST | https://localhost:44360/api/insertRecLoadData/insert
Make sure you set breakpoints in your controller. Not sure how you have setup your project but I'd suggest reading up more on how to setup a Web API using ASP.NET Core. Look into RESTful design to also get an idea on how to best setup these end points.
Good luck!
The current route configuration on your controller and on your action will result in duplicated section in your route. Specifically, the route the action will be associated with will be "api/RecLoadPrime/RecLoadPrime/insertRecLoadData/{RecStartDate}/{RecEndDate}".
Consider removing the RecLoadPrim/ prefix from your action route attribute as follows:
[Route("insertRecLoadData/{RecStartDate}/{RecEndDate}")]

Binding single value from the body of a POST with MVC

I am implementing a protocol in ASP.NET MVC, and I need to be able to bind data from a request made like this:
curl -H "Content-Type: application/json" -d 'true' http://example.com/operation/some_id
I have tried using the [FromBody] attribute on the parameter in my controller, like this:
public ActionResult Operation(string id, [FromBody] bool setSomething)
The above code does not work, as it throws an exception when MVC attempts to set setSomething to null. If I change setSomething to a string, it always gets set to null, so I am unable to parse it to a bool in the action.
I don't have the luxury of changing 'true' to '=true' as I have read elsewhere when similar questions were asked. The POST generated from the curl command above is set in stone. What I need is some way of taking the value true (which is valid json, even without a key) from the body of the POST and assigning it to setSomething. I also need to do this in a way that doesn't prevent me from assigning some_id to id, as I already have working with a custom route.
Does anyone know how this can be accomplished in MVC or Web API?
Any help is appreciated.
I found a solution, but it's more of a workaround.
When the body of a POST is just true or false but nothing more, there is no key to bind this value to. Therefore, MVC doesn't really have anything it can do other than run the value through a JSON deserializer, which succeeds without setting any parameters in the action.
In the end, I had to read the value directly from Request, as described in MVC controller : get JSON object from HTTP body?.
I ended up using similar code:
public ActionResult Operation(string id)
{
var req = Request.InputStream;
req.Seek(0, SeekOrigin.Begin);
string rawBody = new StreamReader(req).ReadToEnd();
bool setSomething = false;
if(bool.TryParse(rawBody, out setSomething))
{
// Do something with 'setSomething'
return Json(new { id = id, status = setSomething });
}
throw new ArgumentException(string.Format("{0} is not a valid boolean value", rawBody));
}
As you can see, I removed setSomething from the parameter list entirely, and rely on reading the raw input stream of the request in order to get the value. This is in no way elegant, and does not make use of all the goodies we get from the MVC framework, but it works.

Why does ASP.NET Web Api model binding uses the parameter type to determine the source of the value?

Since a few days I'm trying to create my own web api controller. Duo to the rest conventions I need to use a post request to create an object. To get concrete, Im having this controller with this action:
public class ReservationController : ApiController
{
[HttpPost]
public void Create(int roomId, DateTime arrivalDate)
{
//do something with both parameters
}
}
This code is not working when I fire a post request at it, I'm receiving a 404 exception something like this:
No action was found on the controller 'Some' that matches the request.
The reason for it is that simple types are read from the query string, complex types from the body, according to this aricle. The web api uses the parameters to match the action to a request and can't therefore map my action to the request.
I do know that I can use the [frombody] tag, but you can only apply that to one parameter and I have 2. I also know that I can create a wrapper object which have both the parameters, but I'm not willing to use wrappers for all my calls.
So I do know that I can work around this by these methods. I also think that this is caused by the fact that the body of the post request can only be read once. But my actual question is:
Why is the source of a parameter determined by it's type and not by it's availability, especially when the conventions state that you should make for example a post request for creation? In MVC this is the case, why isn't it in the web api?
Best regards,
BHD
FINAL UPDATE
Since I'm getting some upvotes, problably more people are facing the same question. In the end it comes to this: Web-Api != MVC. It's simply not the same thing and the web api team made different design decisions than the mvc team I guess.
It seems that you have a fundamental misunderstanding of how Web API actually works.
Web API routing is driven off of verbiage, not the method names. "SomeMethod" actually translates to zero useful information for Web API. As a result, if I post
api/some/some?id=1
OR
api/some/somemethod?id=1
OR EVEN
api/some/?id=1
and the SomeMethod endpoint is the ONLY available POST, it will hit that endpoint.
As such, first of all, make sure you have only one POST on that api controller. If you do, POSTing to it from any test client using either of the query strings above will work just fine.
You can use the [FromBody] attribute on the parameter to force it to read from the body of the HTTP POST instead of the Uri. This is opposed to the [FromUri] attribute which does the opposite.
[HttpPost]
public void SomeAction([FromBody] int id)
{
//do something with id
}
Are you sure you're actually putting the id in the body? It could also be a routing issue. If this still doesn't work then maybe you should use Fiddler and copy the RAW output of your HTTP message here.
If you're packing multiple values into the body such as with JSON then you should use a model which should automatically be deserialized to:
public class PostModel
{
public int ID { get; set; }
public int SomeOtherID { get; set; }
}
[HttpPost]
public void SomeAction(PostModel postModel)
{
//do something with postModel.ID and postModel.SomeOtherID
}
You can actually do this straight out of the box in WebAPI, at least in 2.2 (.Net version 4.5.2). Your controller is correct. Using your controller, if you call it with a HTTP POST like this (tested through Fiddler):
http://localhost:58397/api/Reservation?roomId=123&arrivalDate=2015-12-17
You'll get the correct values of roomId = 123 and arrivalDate = 17.12.2015.
I suspect there's something wrong in your call to the WebAPI. Maybe post that call if you're still not getting it to work.

How do I pass an object in a WebAPI PUT?

I'm new to ASP.NET MVC and it's my first time working with an API.
I'm trying to do a PUT, given an object. However, after starting the application and looking at the available API, it shows my PUT URL as the following, without any option for arguments.
/api/File
Shouldn't it be something like /api/File/{}?
Controller
[HttpPut]
public void PutFile (FileData file)
{
...
}
If I'm doing this completely wrong, please let me know!
That URL is correct since the object you are sending should be passed in the body of the request with the correct content type.... probably multipart/form-data if you are uploading a file. If FileData is not a file and just a complex object then you could use application/x-www-form-urlencoded for forms or application/json for AJAX.
tforester answer is correct, but just to add. You need to use the FromBodyAttribute to tell webapi that the non primitive object (e.g. FileData) is expected and it's in the body of the incoming request. e.g.
[HttpPut]
public void PutFile ([FromBody]FileData file)
{
...
}

Categories

Resources