I am making an HTTP POST method to get data. I have an idea to create a method to get a specific arguments but when I don't have idea to get the arguments taken. In HTTP GET, arguments are in the URL and is more easy to get the arguments. How do I create a method to take all the data in HTTP Post? In PHP for example when you show the var $_POST you show all data in the body post. How can I do this in C#?
My method is this:
[HttpPost]
[AllowAnonymous]
public IHttpActionResult Test()
{
// Get URL Args for example is
var args = Request.RequestUri.Query;
// But if the arguments are in the body i don't have idea.
}
Web API has a feature which automatically binds argument posted to an action inside a controller. This is called Parameter Binding. It allows you to simply request the object inside the URL or the body of the POST request, and it does the deserialization magic for you using a thing called Formatters. There is a formatter for XML, JSON, and other known HTTP request types.
For example, lets say I have the following JSON:
{
"SenderName": "David"
"SenderAge": 35
}
I can create an object which matches my request, we'll call it SenderDetails:
public class SenderDetails
{
public string SenderName { get; set; }
public int SenderAge { get; set; }
}
Now, by receiving this object as a parameter in my POST action, I tell WebAPI to attempt to bind that object for me. If all goes well, I'll have the information available to me without needing to do any parsing:
[Route("api/SenderDetails")]
[HttpPost]
public IHttpActionResult Test(SenderDetails senderDetails)
{
// Here, we will have those details available,
// given that the deserialization succeeded.
Debug.Writeline(senderDetails.SenderName);
}
If I get you Correctly, in C# you use the [HttpPost] attribute to expose a post method.
[HttpPost]
public IHttpActionResult Test()
{
// Get URL Args for example is
var args = Request.RequestUri.Query;
// But if the arguments are in the body i don't have idea.
}
Related
I tried to send a JSON object with the same name that action argument has but It seems not working I don't know what I did wrong.
My question is how to bind simple types like the example shown, without the need to create complex type that wrap my value property?
Action
public IActionResult Test([FromBody] string value)
{
}
PostMan : raw > JSON
{
"value":"testValue"
}
public class MyRequest {
public string Value { get; set; }
}
//controller
IActionResult Test([FromBody] MyRequest request)
This should do the job.
The class/type related to the frombody object should match the whole json object and not only one property of it
This cannot work with GET requests, so try a POST
I have the following api definition
[RoutePrefix("api/lead/1.0")]
public class LeadController:ApiController
{
[Route("/{id:integer}/request-td")]
[HttpPost]
public IHttpActionResult SubmitLead(int id,
FromBody]FormData FormData)
{
}
}
When my QA department is testing this code, they are calling
/api/lead/1.0/12345/request-td
with a valid body and everything passes
However if they change the id from an int to a string,
/api/lead/1.0/invalidid/request-td
They are getting back an iis 404 message.
As a temp solution I have changed the id from an int to a string, and removed the definition from the route.
Within the controller is performing a TyParse to make sure that a valid integer has been passed in the url
However this solution is not very elegant for me
Is there any way so that i can leave the id field defined as an int, with the correct type defined in the route, trap the invalid api request, and then send back my own custom error. ie different http status code and message body
If I have the signature as an int, but with no variable definiton in the route,
[Route("/{id}/request-td")]
[HttpPost]
public IHttpActionResult SubmitLead(int id,
FromBody]FormData FormData)
It is sending back too much information as to why the request is invalid
What I have seen so far, is that you need to get the definitions created in the correct order in global.asax not how to trap invalid api requests and return my own response
If you want receive "string" or "int" as request parameter then you need to write multiple time your "Route" attribute to add it in routing map.
Following is solution to receive values.
[Route("{id:int}/request-td")]
[Route("{id:alpha}/request-td")]
This will accept both "string" and "int" request and don't 404 page.
Hope this code will help you.
I would actually create a custom attribute.
public class CheckArgumentTypeAttribute : ActionFilterAttribute
{
private readonly string ActionArgumentName;
public CheckArgumentIsPositiveAttribute(string actionArgumentName)
{
if (string.IsNullOrWhiteSpace(actionArgumentName)) throw new ArgumentException(nameof(actionArgumentName));
ActionArgumentName = actionArgumentName;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
var keyValuePair = actionContext.ActionArguments.FirstOrDefault(x => x.Key.Equals(ActionArgumentName, StringComparison.OrdinalIgnoreCase));
if (keyValuePair.Equals(default(KeyValuePair<string, object>)) || !int.TryParse(keyValuePair.Value, out in result))
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(JsonConvert.SerializeObject(new YourClass())), Encoding.UTF8, MimeTypes.Application.Json)
};
}
}
}
Then I would decorate my action methods as
[CheckArgumentType("id")]. It is possible to create dynamic error message based on the ActionArgumentName or to further extend this attribute.
In VS2017 I created new ASP.NET Core Web Application
I sample code Get methods works perfectly, but
POST method always receives null as parameter.
This method:
[HttpPost]
public void Post([FromBody]string value)
{
System.Diagnostics.Debug.Print("\n'" + value + "'\n");
}
I tried:
curl -X POST -H "Content-Type: application/json" -d '{"msg": "hello"}' localhost:57084/api/values
In Post value is null.
Tried use POSTMAN: with all possible combinations of content type and message body. NULL as input.
Tried to pass value with '=' as someone suggested. Same, null.
Tried to use dynamics.
public void Post(dynamic value)...
, Value is null.
Tried to use
public void Post([FromBody] HttpRequestMessage request)...
-- same, null.
When I used
[HttpPost]
public void Post(HttpRequestMessage request) // [FromBody]
{
string body = request.Content.ReadAsStringAsync().Result;
var headers = request.Headers;
}
Request had properties: Method: "GET", RequesrURL=null, Headers=null, Content=null
It might be something small and stupid, but I cannot seems to find it yet.
Create a model to hold the data sent to the controller action.
For example, given the following JSON
{"msg": "hello"}
A strongly typed model would look like
public class MyModel {
public string msg { get; set; }
}
Then refactor the controller to expect that model
[HttpPost]
public IActionResult Post([FromBody]MyModel model){
//...access the model
return Ok(model); //echo
}
The request parameter passed through the request is different from the expected one.
If you define your action expecting a string parameter called "value", the expected parameter when you call the request will be:
{ "value" : "something here" }
I have a webservice to authentificate my user (from an app made with cordova) :
[RoutePrefix("api/RegisterUser")]
public class UsersController : ApiController
{
[HttpPost]
[Route("Authenticate")]
public async Task<IHttpActionResult> GetUserAuthenticated(string userName, string pwd)
{
//Code here
}
}
with cordova I use this :
let data = {
FK_BaseID:2,
FK_UserTypeID: this.IDs.userType,
Username: regData.email.toLowerCase(),
Password: regData.password,
}
this._http.post('RegisterUser', data);
But I get this error :
"Message":"No HTTP resource was found that matches the request URI '..../RegisterUser/Authentificate'.",
"MessageDetail":"No type was found that matches the controller named 'RegisterUser'."
My service was working fine with HttpGet but I can't manage to make it work with post.
I've tried something like this :
public IHttpActionResult GetUserAuthenticated(string body)
{
return Ok(body);
}
using [FromBody] also, but I'm unable to make it work. I've seen a few examples using Request.Form to navigate through the body of the request and thus the parameters. But my Request hasn't any Form property.
What am I missing ?
I would wrap the "body" param with a class or struct typedef
the way that the routing mechanism works is by matching methods and parameters received to those one who already been registered (see ApiExplorer class for instance in WebApi; this is how the asp App is actually booting itself into a full state web "actions receiver and invoker")
What happens with your code is, in my humble understanding, is that the app is searching for a complex parameter to build the "body" parameter since it is a POST message (hence the attribute name "FromBody") and thus fails to find an entry in the BODY sector of the http message for a complex "body" segment
just wrap the parameter with a class, i.e
public class MyMessage { public string Body {get;set;} }
notice here that this is actually the same as your code since the "MyMessage" identifier is actually not anywhere to be seen in the body of the message, and is a good example to understand since the web app mechanism actually does this kind of "pairing" on its own based on the url you have provided and the compile time signatures you have imposed on the action implementation
I'll do it like this :
public class AuthenticateViewModel {
public string Username {get; set;}
public string Password {get; set;}
}
And in your controller :
[RoutePrefix("api/RegisterUser")]
public class UsersController : ApiController {
[HttpPost]
[Route("Authenticate")]
public IHttpActionResult GetUserAuthenticated([FromBody]AuthenticateViewModel model){
var userName = model.Username;
var pwd = model.Password;
// Code logic here.
return Ok(model);
}
}
The names of your parameters have to match for the binding to execute.
You may want to check your client code also, seems like you're not posting on the right url :
let data = {
FK_BaseID:2,
FK_UserTypeID: this.IDs.userType,
Username: regData.email.toLowerCase(),
Password: regData.password,
}
this._http.post('api/RegisterUser/Authenticate', data);
I've been trying to figure this out for hours now but none of the solutions seem to help. I have an MVC6 project with AngularJs. I am able to connect, so my routes are working, and I am able to get data back if I hard code a string or something, but I can't seem to access the data sent to the server.
My angularjs http request code:
var app = angular.module('APIService', []);
app.factory('APIService', function ($http) {
var api = {};
api.getBuyer = function (data) {
return $http.post('/api/buyer', data);
}
return api;
});
The angularjs function call
APIService.getBuyer({ Url: 'samirbashir5739', FirstName: 'Samir' }).success(function (res) {
});
My C# Controller
namespace Reporting.api
{
[Route("api/buyer")]
public class BuyersController : Controller
{
// POST api/buyer
[HttpPost]
public string Post([FromBody] string Url)
{
return Url;
}
}
}
I've tried setting the data as "JsonResult data", or even "string Url." Most tutorials I found had an object for the data so it would fit into something like "[FromBody] Buyer buyer" but I don't have an object for it, I simply want the data. Is it possible?
WebApi does not support multiple parameter binding from a post request. You can check more details here.
So the proper way for the WebApi is to create a request model that will contain all the properties that will be bound. Perhaps you can try multiple [FromUri] parameters, but then you will have to add them to the url yourself in angualr, rather than just pass to .post.
Example model:
public class RequestModel
{
public string Url {get;set;}
public string Name {get;set;}
}
I also believe that adding the model improves the structure of your code as you always know what your server expects rather than working with some dynamic data.
P.S. Did not notice that you use ASP.Net Core, my data is from web api 2, but perhaps it's still valid, so you will need to create a model + FromBody should not be required on post requests since it's the default behavior.
I think your controller is wrong. You are trying to pass a Url and a name whereas your controller method is waiting for a single Url.
Try to pass only a Url and it should work.
If you want to pass the Url and the Firstname, you have to modify your controller method like this :
[HttpPost]
public string Post([FromBody] string Url, string FirstName)
{
// Do whatever you need to do here ...
}