Getting angular ajax data in asp.net mvc6 - c#

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

Related

asp.net - how to get value from post method

In asp.net app i receive a form values from angular app.
[HttpPost]
public IActionResult addChange([FromBody] Change change)
{
return Json(change.Status);
}
How to get object or some value of object to use it in another class?
You should be able to access property like that: change.PropertyName.
But you might send data from angular as FormData, then you should change [FromBody] to [FromForm].
It's most likely that you are doing something wrong at angular site. You should check this endpoint via postman.
Edit:
To use this object in another method you should pass it through.
[HttpPost]
public IActionResult addChange([FromBody] Change change)
{
AnotherMethod(change);
return Json(change.Status);
}
public void AnotherMethod(Change change)
{
var foo = change.Status;
}

MVC WebApi Routing with field type specified in route

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.

Post int directly to web api without model class

I am new to C#. I tried to create a post service with using int. All get and post service are working fine.
But when I pass parameter to post service, it's always null. But after creating a class it works fine. Can we pass direct int to service or we must have to create a model class for it?
[System.Web.Http.HttpPost]
public IHttpActionResult GetUserByID(int id)
{
var user = userList.FirstOrDefault((p) => p.Id == id);
if (user== null)
{
return NotFound();
}
return Ok(user);
}
but it always send 0 . but when i create a class and add that int as attribute it works fine.
Working code
[System.Web.Http.HttpPost]
public IHttpActionResult GetUserByID(data id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return Ok();
}
public class data
{
[Required]
public int id { get; set; }
}
Edit
are my header accurate?
I think you need to add [FromBody] to the parameter:
[System.Web.Http.HttpPost]
public IHttpActionResult GetUserByID([FromBody]int id)
{
var user = userList.FirstOrDefault((p) => p.Id == id);
if (user== null)
{
return NotFound();
}
return Ok(user);
}
According to the docs: Parameter Binding in ASP.NET Web API
By default, Web API uses the following rules to bind parameters:
If the parameter is a "simple" type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from
a string.
For complex types, Web API tries to read the value from the message body, using a media-type formatter.
It goes on to say: Using [FromBody]
To force Web API to read a simple type from the request body, add the [FromBody] attribute to the parameter
UPDATES - to get [HttpPost] working...
As #Shahbaz suggested below, make sure that you've got the Content-Type header set to application/json, otherwise you will get error message saying:
The request entity's media type 'text/plain' is not supported for this resource.
Also, make sure you're posting just the id in the Request Body e.g. 1, as opposed to posting the id wrapped in a JSON object as a key/value pair { "id": "1" }.
FINALLY - consider using [HttpGet] instead...
It's worth pointing out, because you are now just sending a single int to get a single record, even if you can get this working using [HttpPost], it's still probably best to change it to [HttpGet] which is semantically correct - you are getting a user record, and don't actually need to post anything at all. So something like this might be better:
[System.Web.Http.HttpGet]
[Route("api/users/{id}")]
public IHttpActionResult GetUserByID(int id)
{
var user = userList.FirstOrDefault((p) => p.Id == id);
if (user== null)
{
return NotFound();
}
return Ok(user);
}
Then put your id in the request URL, something like:
https://yourdomain/api/users/1
The above example makes use of Attribute Routing which can help you create your own custom URLs to target your own API Action Methods.
Send Data to Web Api by a Jquery Like Below :
function PostSth(fid){
$.ajax({
url: apiBaseUrl + 'api/Controller/ActionMethod',
type: 'Post',
data:`'`+fid+`'`,
contentType: "application/json; charset=utf-8",
success: function (data) {
alert(data);
},
error: function () {
alert('Error');
}
});
}
Don't Forget
data:`'`+fid+`'`,
above.
and do in the Code Behind Part :
public string ActionMethod([FromBody]int fid)
{
string result = string.Empty;
//TODO: Your Code
return result;
}

Post service ASP.NET

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

How define and call an api Post in c#

I have created POST/GET request in MVC before.
In my HomeController
[HttpPost]
public string Index(int Value)
{
return Value.ToString();
}
And setting chrome extension POSTMAN with a form-data
I can call http://localhost/mvcApp/ with a variable 'Value' with value '1' and get a string '1' in return
But when I create a surveyController : ApiController doesn't work when I call http://localhost/mvcApp/api/survey/
public string Post(int Value)
{
return Value.ToString();
}
"Message": "No HTTP resource was found that matches the request URI 'http://localhost/mvcApp/api/survey/'.",
"MessageDetail": "No action was found on the controller 'survey' that matches the request."
I'm not sure if the error is in the way the api is created, or in the way the POSTMAN is trying to call the api. Because that '.'
Also try in my HomeControler Index
client.BaseAddress = new Uri("http://localhost/mvcApp");
var result = client.PostAsync("/api/survey", new
{
Value = 1
}, new JsonMediaTypeFormatter()).Result;
if (result.IsSuccessStatusCode) // here return Not found
The WebApi controllers' conventions are not the same as those of plain ol' MVC controllers.
Basically the problem is that you can't specify the int parameter the way you did.
Try this in you WebApi controller:
// nested helper class
public class PostParams {
public int Value { get; set; }
}
public string Post(PostParams parameters) {
return parameters.Value.ToString();
}
and see how that works.
Here's a thorough article on passing parameters within POST requests to WebAPI controllers:
Passing-multiple-POST-parameters-to-Web-API-Controller-Methods
Long story short, these are the conventions, roughly speaking:
you can't capture POST form name-value pairs in parameters
you can capture them inside the properties of a class if that class is the parameter type of one of your method's parameters
you can capture query parameters in method parameters
EDIT
If you wish to test your WebAPI server using C# you could follow these steps:
Create a nice Console Application (preferably within the same solution)
Add the Web API Client NuGet package to this Console Application
Make your Program.cs do something like this.
The following code uses the C# 5.0 async and await operators.
It also uses the Task class and anonymous types.
I've pointed out official MSDN articles (click on the links) should you be interested in what those things are.
using System.Net.Http;
using System.Threading.Tasks;
namespace ConsoleApplication1 {
class Program {
public static void Main(string[] args) {
Test().Wait();
}
private static async Task Test() {
HttpClient client = new HttpClient();
await client.PostAsJsonAsync(
"http://localhost/mvcApp/api/survey/",
new {
value = 10
}
);
}
}
}
This wasnt easy. After lot of reading I solve it like this.
First the api controler need to define the input parameter with the [FromBody] attribute
// POST api/survey
public void Post([FromBody]string value)
{
}
For testing I put a button in the view and use an Ajax / Post, the variable name need to be an empty string before the variable value.
$(document).ready(
$('#post').click(function () {
var url = 'http://localhost/mvcApi/api/survey';
var data = { "": 'Hola' }; // Define a simple variable this way
$.ajax({
type: "POST",
url: url,
data: data,
success: sucess
}
});
})
Or if you want send mutliple values
data = { "": ["update one", "update two", "update three"] };
But if you want receive an object
public void Post(Survey data)
{
string value = data.Value.ToString();
}
$('#post').click(function () {
....
var data = { value: 25 }
More info here Sending Data and here Binding

Categories