Can a web api post method contains more than one parameter? - post

I have this method in a controller in a web api
public class UserController : ApiController
{
[HttpPost]
public HttpResponseMessage Login(string name, string password){
//dothings
}
}
but if I try a ajax request:
$.ajax({
type: "POST",
url: "http://localhost:19860/Api/User/Login",
data: { name: "name", password: "12345" },
success: succ,
error: err
});
It gives me the error:
Message: "No HTTP resource was found that matches the request URI 'http://localhost:19860/Api/User/Login'."
MessageDetail: "No action was found on the controller 'User' that matches the request."
but, if i remove the parameters it works!
public class UserController : ApiController
{
[HttpPost]
public HttpResponseMessage Login(){
string name= HttpContext.Current.Request.Params["name"];
string password= HttpContext.Current.Request.Params["password"];
// name and password have the value that i passed in the ajax call!!
}
}
why it is like this?
For reasons unrelated to this question, I can't change the web api, so I have to mantain the:
public HttpResponseMessage Login(string name, string password)
format.
Can I mantain this format and be able to make the ajax call?

You can not post multiple parameter to Web-API methods as mentioned in this blog post
There is few alternative to this as follow.
1. Try to pass it on queryString.
Change your ajax function call like below.
$.ajax({
type: "POST",
url: "http://localhost:19860/Api/User/Login?name=name&pass=pass",
data: { name: "name", password: "12345" },
success: succ,
error: err
});
you will get name = name and password = pass in your api controller.
2. Create your own binder as implemented in this blog post.
3. Wrap all the Web-API method parameter in one object or type (recommended).
From the above you can use 1st and 2nd approach as you mentioned in your question you can not change your api method signature.

Related

Cannot post to asp.net MVC controller

Here is my request definition:
params = typeof params !== "undefined" ? params : {};
var deferred = $q.defer();
var response = $http({
method: "post",
dataType: "json",
data: JSON.stringify(params),
headers: {
'Content-Type': "application/json; charset=utf-8",
},
url: 'apiUrl' + 'MVCcontrollerName' + '/'
});
And C#:
public class MVCcontrollerName : Controller
{
public ActionResult Index(int? Id = null)
{
.......
I am not getting my Id parameter in the controller class. It's null. The parameter is defined as {Id:1}.
I also tested POST with Chrome REST client with the same results.
Any idea, what's wrong?
I see a couple of issues here.
First the name of your controller should follow the convention of SomenameController, so you might want to call it MVCcontrollerNameController
The URL you specify is missing a slash to delimit between the path and the controller name:
url: 'apiUrl' + '/' + 'MVCcontrollerName' + '/'
Or simply:
url: 'apiUrl/MVCcontrollerName/'
Or more correctly, let MVC do the routing for you (as suggested by #JamieRees):
url: '#Url.Action("Index","MVCcontrollerName")'
3. The main issue however, is that you are POSTing the data where you really should be calling it as a GET with the parameter as part of the URL. However, if you really need to have it in a POST, then you need to decorate the parameter with the FromBody attribute:
public ActionResult Index([FromBody]int? Id = null)
I'm not sure if this will help, but anyway you should have attribute [HttpPost] on action that will be called with post

Web API uri to routed and defined action returns 404

I don't understand how System.Web.Http method attributes are used. I have this method Logon in my Auth web API controller (ASP.Net MVC 4):
public HttpResponseMessage Logon(string email, string password)
{
User user = UserSrv.Load(email);
if (user != null && user.CheckPassword(password))
return this.Request.CreateResponse<User>(HttpStatusCode.OK, user);
else
return this.Request.CreateResponse(HttpStatusCode.BadRequest, "Invalid username or password");
}
The WebApiConfig.cs file is default:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.Remove(config.Formatters.XmlFormatter);
}
}
As is, a GET method returns 405 method not allowed. As do a PUT, HEAD and all other verbs I've tried. But for a POST, it returns a 404 not found with the following JSON:
{
"Message": "No HTTP resource was found that matches the request URI 'http://localhost:8080/api/Auth/Logon'.",
"MessageDetail": "No action was found on the controller 'Auth' that matches the request."
}
If a add [HttpPost] attribute before method definition, I get the exact same results. With HttpGet, of course GET requests are OK. Combination of both attributes doesn't change anything. How come POST requests are not correctly routed?
Edit:
The POST request does not match because the Uri is http://localhost:8080/api/Auth/Logon, with no query parameter. If I set default values for the email and password parameters, the method matches. But I thought that MVC was smart enough to match the request content to the action parameters. Do I really need to read the content stream to find the argument values?
It's apparently impossible with web Api to bind a post request with multiple parameters to an action. The easiest solution is to send a json object and parse it. My method now looks like
[HttpPost]
public HttpResponseMessage Logon(JObject dto)
{
dynamic json = dto;
string email = json.email;
string password = json.password;
User user = UserSrv.Load(email);
if (user != null && user.CheckPassword(password))
return this.Request.CreateResponse<User>(HttpStatusCode.OK, user);
else
return this.Request.CreateResponse(HttpStatusCode.BadRequest, "Invalid username or password");
}
See http://www.west-wind.com/weblog/posts/2012/May/08/Passing-multiple-POST-parameters-to-Web-API-Controller-Methods
and http://www.west-wind.com/weblog/posts/2012/Sep/11/Passing-multiple-simple-POST-Values-to-ASPNET-Web-API

how to pass data from View to Controller using ajax get or post in mvc with parameters

I am trying to pass data from View to Controller Action Method using ajax as follows:-
I have Membership instance of user which I passed in from another controller to this view below using viewbag somewhat like this ViewBag.MyUser = MyUser;
Now I want to pass 'MyUser' to another Controller form this view using ajax as below.
$('#Link').click(function () {
$.ajax({
url: http://localhost/Account/Process,
type: 'POST',
data: '#ViewBag.MyUser',
success: function () {
},
error: function () {
}
});
The ActionMethod to which I am posting is as follows
public ActionResult Process(MembershipUser MyUser)
{
//Do somethihng with MyUser
}
If I pass do ajax post, I get error internally at BeginExecuteCore(AsyncCallback callback, object state) stating that 'No parameterless constructor defined for this object.' and control does not even comes to my actionmethod.
If I remove the parameter (MembershipUser MyUser) from Action Method it posts to Action method, but then
how can i pass 'MyUser' in such case without parameter from that view to controller ?
is there something wrong with routes ? if yes what should be the route ?
or should i use get or post ?
Where should i cast the MyUser back to MembershipUser ?
The problem is you can't pass MyUser as parameter from JQuery because JQuery doesn't know the class MembershipUser. Remember that JQuery is a client side language and MembershipUser is defined in C# on the server side.
You could pass the properties that you need from the MyUser object to the Process action using GET as follows (supossing that the MyUser object has and ID an a Name):
$('#Link').click(function () {
$.ajax({
url: http://localhost/Account/Process,
type: 'GET',
data: {
id: "#ViewBag.MyUser.ID",
name: "#ViewBag.MyUser.Name"
},
success: function () {
},
error: function () {
}
});
The action should be something like this:
public ActionResult Process(int id, string name)
{
//Do something
}
I hope it helps you!

MVC ajax json post to controller action method

I am trying to achieve a JQuery AJAX call to a controller action method that contains a complex object as a parameter.
I have read plenty blogs and tried several techniques learned from these. The key post on which I have constructed my best attempt code (below) is the stackoverflow post here .
I want to trigger an asynchronous post, invoked when the user tabs off a field [not a Form save post – as demonstrated in other examples I have found].
My intention is to:
Instantiate an object on the client [not the ViewModel which provides the type for the View];
Populate the object with data from several fields in the view;
Convert this object to JSON;
Call the controller action method using the jQuery.Ajax method, passing the JSON object.
The results will be returned as a JSON result; and data will be loaded into fields in the view depending on results returned.
The problems are:
If the action method is attributed with the HttpPost attribute, the controller Action method is not invoked (even though the AJAX call type is set to ‘POST’).
If the action method isattributed with HttpGet, the values of properties of the parameter are null
The ReadObject method throws the error: "Expecting element 'root' from namespace ''.. Encountered 'None' with name 'namespace'".
Hopefully someone can help. Thanks. Code below:
Client js file
var disputeKeyDataObj = {
"InvoiceNumber": "" + $.trim(this.value) + "",
"CustomerNumber": "" + $.trim($('#CustomerNumber').val()) + ""
};
var disputeKeyDataJSON = JSON.stringify(disputeHeadlineData);
$.ajax({
url: "/cnr/GetDataForInvoiceNumber",
type: "POST",
data: disputeKeyDataJSON,
dataType: 'json',
contentType: "application/json; charset=utf-8",
success: EnrichedDisputeKeyData(result)
});
Action Filter and class for the type associated with the Action method parameter
[DataContract]
public class DisputeKeyData
{
[DataMember(Name = "InvoiceNumber")]
public string InvoiceNumber { get; set; }
[DataMember(Name = "CustomerNumber")]
public string CustomerNumber { get; set; }
}
Action method on the controller
//[HttpPost]
[ObjectFilter(Param = "disputeKeyData", RootType = typeof(DisputeKeyData))]
public ActionResult GetDataForInvoiceNumber(DisputeKeyData disputeKeyData)
{
//Blah!
//....
return Json(disputeKeyData, JsonRequestBehavior.AllowGet);
}
Below is how I got this working.
The Key point was:
I needed to use the ViewModel associated with the view in order for the runtime to be able to resolve the object in the request.
[I know that that there is a way to bind an object other than the default ViewModel object but ended up simply populating the necessary properties for my needs as I could not get it to work]
[HttpPost]
public ActionResult GetDataForInvoiceNumber(MyViewModel myViewModel)
{
var invoiceNumberQueryResult = _viewModelBuilder.HydrateMyViewModelGivenInvoiceDetail(myViewModel.InvoiceNumber, myViewModel.SelectedCompanyCode);
return Json(invoiceNumberQueryResult, JsonRequestBehavior.DenyGet);
}
The JQuery script used to call this action method:
var requestData = {
InvoiceNumber: $.trim(this.value),
SelectedCompanyCode: $.trim($('#SelectedCompanyCode').val())
};
$.ajax({
url: '/en/myController/GetDataForInvoiceNumber',
type: 'POST',
data: JSON.stringify(requestData),
dataType: 'json',
contentType: 'application/json; charset=utf-8',
error: function (xhr) {
alert('Error: ' + xhr.statusText);
},
success: function (result) {
CheckIfInvoiceFound(result);
},
async: true,
processData: false
});

ajax call to C# method not working

I want to call a method in C# from a client side AJAX/JQuery message.
The client code is:
function TestClickFunc(userId) {
$.ajax({
url: "/Users/UpdateEmailDistributionListFlag",
type: "POST",
data: { "userId" : userId },
success: function (data) { alert(data); }
});
}
This method gets called with the correct parameter.
However in my UsersController, this method does not get called;
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult UpdateEmailDistributionListFlag(int userId)
{
// db update
return View();
}
Can you see why?
Check if you have a route with parameter userId. In the default route the parameter's name is id, not userId so your method will not be found.
What I found was that the CDN was not working;
I can't see the spelling mistake, but it worked when I used my local script!

Resources