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);
Related
I've created a simple .NET core controller and am trying to query it from Angular.
The problem is that whenever I send a post request to my login action, I always receive null parameters! The weird thing though is that upon investigating, the request body/payload clearly shows my parameters are set.
See my screenshot for an example of what I mean.
I have tried my best and have been reading through similar problems for the past few hours, but haven't had any success ðŸ˜
So my question is: What is causing my null parameters? .NET core must be doing something that I'm not aware of.
If you think seeing more code or the entire project would help, don't hesitate to ask!
You can't get two separate parameters from body, cause it is going to bind email and password as properties of the parameter, not exact parameters. You need to create an object, which defines properties with those names, and use that object in the parameter
class Login
{
public string Email { get; set; }
public string Password { get; set; }
}
In the action method parameter
([FromBody]Login model)
I suggest to create class with properties email and password.And using [FromBody] attribute.
public class LoginModel
{
public string Email {get;set;}
public string Password {get;set}
}
[HttpPost]
[Route("login")
public async Task<IActionResult> Login([FromBody] LoginModel model)
{
// your code
}
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.
This question already has answers here:
404 error when making a POST call from Angular HttpClient to ASP.NET Core 2.2 Web API (with CORS enabled)
(2 answers)
Closed 3 years ago.
I am just starting to get into netcore 2.2 and I am trying to call my controller from angular as so however when I call the send contact email method from my service I get a "Http failure response for http://localhost:53270/api/DataController/SendContactEmail/: 404 Not Found"
As you can clearly see I am explicitly writing out the route in the below routine
sendContactEmail(contact: ISubmitModel): Observable<any> {
return this._http.post<any>('api/DataController/SendContactEmail/', contact)
}
and my controller method
[Route("api/[controller]")]
public class DataController : Controller
{
[HttpPost("[action]")]
public IActionResult SendContactEmail(ContactUs contact)
{
ContactUs form = new ContactUs()
{
Email = contact.Email,
Name = contact.Name,
Phone = contact.Phone,
Message = contact.Message
};
return Ok(form);
}
public class ContactUs
{
public string Name { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Message { get; set; }
}
}
You should ommit the Controller part and just use Data
return this._http.post<any>('http://localhost:51889/api/Data/SendContactEmail/', contact)
As the others have mentioned you should not be using the word Controller in the route since you're using the Route Attribute with the template [controller] inside the route it'll only take the name of that controller without including the Controller word.
To make it more readable you could change the template api/[controller] for api/data so it doesn't lead to confusion.
This tends to be a good practice, because
if someday in the future you'd decide to change the name of the Controller for some reason, and you've tied the route to the controller name, the consuming applications of the api would break because the route would have changed (you don't want that) and obviously the consuming applications would have to change all the references to that route in order to work properly.
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 ...
}
I have a Web API project... I would like to respect the REST principles, so I should have just a GET method and just a POST method...
I have to do a search, so i think this matches the GET method, because after the search I obtain the result and I show it in the page... If I do not find anything I must create the object... this action is a POST...
Now I have a problem... I must validate the filters of the search, because filters are a tax code and a alpha-numeric code (6 chars)... I have already done a client side validation. Now I should do a Server Side validation.
Untill now, we have used data annotation to validate the request, but this is a GET... so my method has this signature:
[HttpGet]
public IHttpActionResult GetActivationStatus(string taxCode, string requestCode)
{
if (ModelState.IsValid)
{
...
}
}
But how can I validate my ModelState with Data Annotation?
Thank you
Create your own model...
public class YourModel
{
[//DataAnnotation ...]
public string taxCode { get; set; }
[//DataAnnotation ...]
public string requestCode { get; set; }
}
And change your signature of your server side controller:
[HttpGet]
public IHttpActionResult GetActivationStatus([FromUri] YourModel yourmodel)
{
if (ModelState.IsValid)
{
...
}
}
If your client side code already worked you don't have to change it... Please, note that the properties of your Model are the same of the parameter you are passing now (string taxCode, string requestCode)... and they are case sensitive...
EDIT:
I mean that you can call your controller in this way:
http://localhost/api/values/?taxCode=xxxx&requestCode=yyyy