0

I am new to MVC and Ajax, but have good working knowledge and experience with C#.

As a test, I have created an ApiController with 4 methods, 2 using GET and 2 using POST.

All 4 methods work perfectly fine with @Ajax.ActionLink, but (to my best knowledge) equivalent $.ajax call fails POST (but passes GET).

Here is my ApiController:

public class ValuesController : ApiController { [HttpGet] // GET api/values public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } [HttpGet] // GET api/values/5 public string Get(int id) { return $"id={id}, ~id={~id}"; } [HttpPost] // POST api/values public string PostData(string value="123") { var x = Request; return new string(value.Reverse().ToArray()); } [HttpPost] // POST api/values public JsonResult<object> PostData(string value, int i) { return Json((object)new { s=new string(value.Reverse().ToArray()), i }); } } 

And the corresponding calls from .cshtml file:

@Ajax.ActionLink("Click me for Get()", "Get", "api/Values", null, new AjaxOptions() { HttpMethod = "GET", OnSuccess = "res0", OnFailure = "fail0", UpdateTargetId = "status0" }) <script language="javascript"> function res0(res) { $('#status00')[0].innerText = "Result: " + res; } function fail0(a, b, c) { $('#status000')[0].innerText = "Error\n" + b + '\n' + c; } </script> <form action="" id="form0"> <input type="submit" /> </form> <div id="status0"></div> <div id="status00"></div> <div id="status000"></div> @Ajax.ActionLink("Click me for Get(5)", "Get", "api/Values", new { id = 5 }, new AjaxOptions() { HttpMethod = "GET", OnSuccess = "res1", OnFailure = "fail1", UpdateTargetId = "status1" }) <script language="javascript"> function res1(res) { $('#status11')[0].innerText = "Result: " + res; } function fail1(a, b, c) { $('#status111')[0].innerText = "Error\n" + b + '\n' + c; } </script> <form action="" id="form1"> <input type="submit" /> </form> <div id="status1"></div> <div id="status11"></div> <div id="status111"></div> @Ajax.ActionLink("Click me for PostData('Hello')", "PostData", "api/Values", new { value = "Hello" }, new AjaxOptions() { HttpMethod = "POST", OnSuccess = "res2", OnFailure = "fail2", UpdateTargetId = "status2" }) <script language="javascript"> function res2(res) { $('#status22')[0].innerText = "Result: " + res; } function fail2(a, b, c) { $('#status222')[0].innerText = "Error\n" + b + '\n' + c; } </script> <form action="" id="form2"> <input type="submit" /> </form> <div id="status2"></div> <div id="status22"></div> <div id="status222"></div> @Ajax.ActionLink("Click me for PostData(\"Hello\", 3)", "PostData", "api/Values", new { value = "Hello", i = 3 }, new AjaxOptions() { HttpMethod = "POST", OnSuccess = "res3", OnFailure = "fail3", UpdateTargetId = "status3" }) <script language="javascript"> function res3(res) { $('#status33')[0].innerText = "Result: " + JSON.stringify(res); } function fail3(a, b, c) { $('#status333')[0].innerText = "Error\n" + b + '\n' + c; } </script> <form action="" id="form3"> <input type="submit" /> </form> <div id="status3"></div> <div id="status33"></div> <div id="status333"></div> <script language="javascript"> document.getElementById('form0').onsubmit = function (e) { e.preventDefault(); form0_submit(); }; document.getElementById('form1').onsubmit = function (e) { e.preventDefault(); form1_submit(); }; document.getElementById('form2').onsubmit = function (e) { e.preventDefault(); form2_submit(); }; document.getElementById('form3').onsubmit = function (e) { e.preventDefault(); form3_submit(); }; function form0_submit() { $.ajax({ type: 'GET', url: '/api/Values/Get', success: function (res) { $('#status0')[0].innerText = res; }, error: function (jqXHR, tStatus, errThrown) { $('#status00')[0].innerText = tStatus; $('#status000')[0].innerText = errThrown; } }); } function form1_submit() { $.ajax({ type: 'GET', url: '/api/Values/Get', data: {id:@DateTime.Now.TimeOfDay.Seconds}, success: function (res) { $('#status1')[0].innerText = res; }, error: function (jqXHR, tStatus, errThrown) { $('#status11')[0].innerText = tStatus; $('#status111')[0].innerText = errThrown; } }); } function form2_submit() { $.ajax({ type: 'POST', url: '/api/Values/PostData', data: JSON.stringify({ value: "Test string" }), contentType: 'application/json; charset=UTF-8', dataType: 'json', success: function (res) { $('#status2')[0].innerText = res; }, error: function (jqXHR, tStatus, errThrown) { $('#status22')[0].innerText = tStatus; $('#status222')[0].innerText = errThrown; } }); } function form3_submit() { // not implemented yet, need to get form2_submit() working } </script> 

I used to get a 404 - Not found error until in public string PostData(string value="123") I made value an optional parameter. Now the function is called with the default "123" value, not what I pass it. In form2_submit() I tried different combinations for the data parameter.

Analysing the response using the Chrome developer tools (right-click->Inspect, then Network tab), I can see that the ActionLink sends the parameters as Query String Parameters, while the $.ajax call does it as Request Payload or Form Data depending on exactly what I pass to data parameter. In both cases, the parameter name/value pair is correct, but the C# ApiController seems to ignore that value (I have tried [FromBody] attribute as well).

I've checked other posts here and on other sites, but cannot figure out why the data is being transmitted differently

Edit:

As requested, the output of Get-Package:

PM> Get-Package Id Versions ProjectName -- -------- ----------- Antlr {3.4.1.9004} WebApplication1 bootstrap {3.0.0} WebApplication1 jQuery {1.10.2} WebApplication1 Microsoft.ApplicationInsights {2.2.0} WebApplication1 Microsoft.ApplicationInsights.Ag... {2.0.6} WebApplication1 Microsoft.ApplicationInsights.De... {2.2.0} WebApplication1 Microsoft.ApplicationInsights.Pe... {2.2.0} WebApplication1 Microsoft.ApplicationInsights.Web {2.2.0} WebApplication1 Microsoft.ApplicationInsights.Wi... {2.2.0} WebApplication1 Microsoft.ApplicationInsights.Wi... {2.2.0} WebApplication1 Microsoft.AspNet.Mvc {5.2.3} WebApplication1 Microsoft.AspNet.Razor {3.2.3} WebApplication1 Microsoft.AspNet.Web.Optimization {1.1.3} WebApplication1 Microsoft.AspNet.WebApi {5.2.3} WebApplication1 Microsoft.AspNet.WebApi.Client {5.2.3} WebApplication1 Microsoft.AspNet.WebApi.Core {5.2.3} WebApplication1 Microsoft.AspNet.WebApi.HelpPage {5.2.3} WebApplication1 Microsoft.AspNet.WebApi.WebHost {5.2.3} WebApplication1 Microsoft.AspNet.WebPages {3.2.3} WebApplication1 Microsoft.CodeDom.Providers.DotN... {1.0.8} WebApplication1 Microsoft.jQuery.Unobtrusive.Ajax {3.2.5} WebApplication1 Microsoft.Net.Compilers {2.4.0} WebApplication1 Microsoft.Web.Infrastructure {1.0.0.0} WebApplication1 Modernizr {2.6.2} WebApplication1 Newtonsoft.Json {6.0.4} WebApplication1 Respond {1.2.0} WebApplication1 WebGrease {1.5.2} WebApplication1 PM> 
5
  • Can you post the output if the following command Get-Package from your NuGet console Commented Apr 26, 2018 at 19:32
  • You are sending the request to /api/Values/Get, try sending a get to /api/Values only. Commented Apr 26, 2018 at 20:54
  • @Hackerman, if it makes a difference, all NuGet packages had been updated to the latest versions, but I will post the output once I get home. Commented Apr 26, 2018 at 21:09
  • @WeslleyRamos, GET works fine, it's POST that is having trouble. I had played with different C# function names and adding ActionName attribute, so I doubt Get (as in the action name) is an issue. I could try if you insist though, but again, it's POST not working, GET is fine... Commented Apr 26, 2018 at 21:12
  • Can you paste here what you see in the network tab of developer tools of your browser? I'm pretty sure that there's a hint of what is going wrong there. edit: (I mean a picture of the screen) Commented Apr 26, 2018 at 22:00

1 Answer 1

1

By default, If the parameter of your web api method is a simple type like string or int, Web API tries to get the value from the uri.

If you are sending the string in the request body, you should explicitly use the FromBody attribute decorator so that web api know it should read the data from the request body. When FromBody attribute is present, web api will use the content-type header value to pick a formatter which will work with the content it received.

[HttpPost] // POST api/values public string PostData([FromBody]string value) { if (value == null) value = "some non null"; var x = Request; return new string(value.Reverse().ToArray()); } 

Now on the client side, make sure you are sending JSON string version of the string you want to send, while specifying the content type as application/json

$.ajax({ type: 'POST', url: '/api/Values/PostData', data: JSON.stringify("My Test string"), contentType: 'application/json', success: function (res) { console.log(res); }, error: function (jqXHR, tStatus, errThrown) { console.log(errThrown); } }); 
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, @Shyju, that indeed was my issue. I tried the last function on my own and stumbled upon Ca't bind multiple parameters ('value' and 'i') to the request's content. exception and found an answer here, which explains why a far more complex object was happily recognised from the body automatically (different project), but a simple string wasn't. I was trying to find documentation for $.ajax, turns out I just had to read more on ApiController

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.