Reputation: 800
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>
Upvotes: 0
Views: 75
Reputation: 218722
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);
}
});
Upvotes: 1