ASP.NET Core POST to MVC - asp.net-core-mvc

I am trying to make a simple POST request from the client to an MVC controller in my ASP.NET Core application. The problem is that even though I have set the ajax call properly (I think), the payload is always submitted as form url encoded and my model on the server ends up null. Here is my setup:
Controller action definition:
[HttpPost]
public async Task<EmailResponse> SendEmail([FromBody] EmailModel model)
{
EmailResponse response = new EmailResponse();
...
return response;
}
Model:
public class EmailModel
{
[JsonProperty("fistName")]
public string FirstName { get; set; }
[JsonProperty("lastName")]
public string LastName { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
[JsonProperty("company")]
public string Company { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
}
Client ajax call:
$.ajax({
type: "POST",
url: "/Home/SendEmail",
contentType: 'application/json; charset=utf-8',
data: model
}).done(function (result) {
...
}).error(function(error) {
...
});
Here is my request:
POST /Home/SendEmail HTTP/1.1
Host: localhost:5000
Connection: keep-alive
Content-Length: 77
Pragma: no-cache
Cache-Control: no-cache
Accept: */*
Origin: http://localhost:5000
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36
Content-Type: application/json; charset=UTF-8
Referer: http://localhost:5000/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: _ga=GA1.1.116706601.1460641478
firstName=Joe&lastName=Doe&email=test%40test.com&company=Acme%2C+Inc&message=
Notice the payload at the end of the request. It is not in JSON format even though I am passing a plain JS object and specifying the contentType as application/json. I am guessing this is why my model is always null on the server.
I have been staring at this for couple of hours now and cannot see where the problem is. Any input is greatly appreciated.
Thank you.

Your model is not serialized to json. The object is serialized to default media type - key value pairs - what is known as "application/x-www-form-encoded".
Try to enforce the JSON
$.ajax({
type: "POST",
url: "/Home/SendEmail",
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(model) //notice the JSON.stringify call
}).done(function (result) {
...
}).error(function(error) {
...
});

Related

POSTing JSON data to WCF Rest Service

I know there are many questions on SO about this but none of the suggestions have worked for me.
Here is my code:
var restService = "http://wcfrestservice:8004/RADPOCService/WebApp1";
$.ajax({
url: restService,
type: "POST",
data: { PhoneNumber: y },
dataType: "json",
contentType: "application/json; charset=utf-8",
success:
function (data) {
window.open(data.Url, '_blank');
}
});
Fiddler shows my request going across the wire like so (redacted):
POST http://localhost:8004/RADPOCService/WebApp1 HTTP/1.1
Host: localhost:8004
Connection: keep-alive
Content-Length: 22
Accept: application/json, text/javascript; q=0.01
Origin: http://localhost:8000
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36
Content-Type: application/json; charset=UTF-8
Referer: http://localhost:8000/Default.aspx
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Requestor: Me
PhoneNumber=1234567890
I am expecting the payload to look like this:
{ "PhoneNumber": "1234567890"}
When I set the payload as above in the Fiddler Composer tab the service works as expected. What am I doing wrong?
I hate to do this so quickly after posting my question but the answer is to use the JSON.stringify API on the data being sent to the service. So the above should have the following code for 'data' in the ajax call:data: JSON.stringify({ PhoneNumber: y })

postman params not sent to web api controller

I have this web api controller:
public class LoginController : ApiController
{
private mw2devnew15Entities db = new mw2devnew15Entities();
[System.Web.Http.HttpGet]
public string Post()
{
string authenticationToken = "";
return authenticationToken;
}
[System.Web.Http.AcceptVerbs("GET", "POST")]
public HttpResponseMessage Post(JObject data)
{
dynamic json = data;
LoginForm loginF = new LoginForm();
loginF.username = json.username;
loginF.password = json.password;
return Request.CreateResponse(HttpStatusCode.OK);
}
}
I'm able to post correctly with this ajax call:
jQuery.ajax({
type: "POST",
url: "http://localhost:5832/api/Login",
data: JSON.stringify({ username: 'joep11aul1234', password: '1212213' }),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
alert(data);
}
});
But when I'm trying to use Postman to place POST call, the JObject is null.
Any idea why?
Using Postman you're not reproducing the same request as your JavaScript code since you posting the parameters in the query string. What you shoud do instead is something like this:
Add a content type header with the value of application/json:
and for your request body select raw and then add your JSON:
this will send the following request same as your JavaScript code:
POST /api/Login HTTP/1.1
Host: localhost:5832
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 4bf25ded-7548-77f9-3389-fa16a5d50087
{ "username": "joep11aul1234", "password": "1212213" }

spring 4 and CSRF

I use spring boot, spring rest and spring security in a single page application.
With spring 4, CSRF is enable by default.
I wrote a class who extend WebSecurityConfigureAdapter
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/rest/**").authenticated();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.formLogin().successHandler(authenticationSuccessHandler);
http.formLogin().failureHandler(authenticationFailureHandler);
http.logout().logoutSuccessUrl("/");
// tried with and without... same issue
http.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);
}
CsrfTokenResponseHeaderBindingFilter come from
https://github.com/aditzel/spring-security-csrf-filter/blob/master/src/main/java/com/allanditzel/springframework/security/web/csrf/CsrfTokenResponseHeaderBindingFilter.java
I use only html (so no jsp, no xhmtl...) page only.
I do ajax call and feed my html.
Request header
POST /login HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 30
Pragma: no-cache
Cache-Control: no-cache
Accept: */*
Origin: http://localhost:8080
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://localhost:8080/
Accept-Encoding: gzip, deflate
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: JSESSIONID=B05ED2676EC1637D74AC7622E018C9FD
In my form i have
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
When I try to log, i get
{"timestamp":1444179957867,"status":403,"error":"Forbidden","message":"Expected CSRF token not found. Has your session expired?","path":"/login"}
When the log fail, this command return null
jqXHR.getResponseHeader('X-CSRF-TOKEN')
ajax call
var data = 'username=' + $('#username').val() + '&password=' + $('#password').val();
$.ajax({
data: data,
timeout: 1000,
type: 'POST',
url: '/login'
}).done(function (data, textStatus, jqXHR) {
window.location = "main.html";
}).fail(function (jqXHR, textStatus, errorThrown) {
});
});
EDIT
In the browser when i connect to localhost:8080.
i see in the header answer
X-CSRF-HEADER:X-CSRF-TOKEN
X-CSRF-PARAM:_csrf
X-CSRF-TOKEN:0d5bf042-a30f-4f2e-a99c-51ed512f811a
How to get this information in JS? I need to put it in my post call.
There isn't enough information to debug this issue. You should post the following information:
Your XML/Java Spring Security Configuration.
Your Spring Web Configuration. Are you using JSPs or XHTML? Are you sure your view model is correct? Maybe your having issues with the web module not sending the token.
The actual http request headers/body. Is the request actually correct or is spring just not picking up the token?

Problems with sending ajax post to controller

I have an action
[HttpPost]
public JsonResult AddLocationsForOrder(List<BuyerOrderLocation> locations, string[] orders)
{
// something here
}
and js code, which sends the request:
data = { locations: serializedLocations, orders: selector.val() };
$.ajax({
url: addLocationUrl,
success: function (responseText) { Core.responceReceived(responseText, null, null); },
error: function () { Core.showErrorNotification("Sorry, some error occured. Please contact administrator"); },
async: false,
type: 'POST',
dataType: 'json',
data: JSON.stringify(data)
});
post details from firebug are here:
locations
"[{"id":225,"country":"United States","countryShort":"US","state":"Iowa","stateShort":"IA","city":null,"zipCode":null,"address":"Douglas, IA, USA","latitude":41.9053851,"longtitude":-93.8349976,"bounds":null,"isDeleted":false,"county":"Boone","radius":0},{"id":226,"country":"United States","countryShort":"US","state":"Iowa","stateShort":"IA","city":null,"zipCode":null,"address":"Iowa, USA","latitude":41.8780025,"longtitude":-93.097702,"bounds":null,"isDeleted":false,"county":null,"radius":0}]"
orders
[ "10440" , "10441" , "10442" ]
0 "10440"
1 "10441"
2 "10442"
And request headers:
Accept application/json, text/javascript, */*; q=0.01
Accept-Encoding gzip, deflate
Accept-Language en-us,en;q=0.5
Connection keep-alive
Content-Length 830
Content-Type application/x-www-form-urlencoded; charset=UTF-8
Cookie ASP.NET_SessionId=ewtvgwleg5or1lqctinpjv2d; .ASPXAUTH=8414A94FE30B8F8D6FE862259398F39D6F6D2EE995C9EE16549987E2E1291851788CAB75425579F61F70EBE4C7B785B07CB36773894A5B2A513966247AA5B670A25D4AE565796B449912D745EBE5E5E4AB8280902C132FC3D97C0C33BA2C2357372CD9C9EA49983DC4A8E875C6E4D653FA049EC7B0F3824666F35D3838226AA19ACEEC1B8C5716E995966787268313FEF90E2ABBAE989CA682D406EBCE361BB7
Host local.attorneyboost
Referer http://local.attorneyboost/Order/OrderInfo/10439
User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; rv:14.0) Gecko/20100101 Firefox/14.0.1
X-Requested-With XMLHttpRequest
As you can see, request type is "POST", and I'm "Jsoning" data before sending to the server. But in action I got a null values for parameters. What am I doing wrong? I'm already stuck on searching for mistake
In this article they explain how to send complex JSON object to ASP.NET.
Hope this helps
You need to specify the datatype in the request send it to the server. The datatype attribute of your called sets the format of the response of the server, to set the datatype in the format that need to send to the server you need to use the contentType attribute
Modify the ajax called with this
$.ajax({
url: addLocationUrl,
success: function (responseText) { Core.responceReceived(responseText, null, null); },
error: function () { Core.showErrorNotification("Sorry, some error occured. Please contact administrator"); },
async: false,
type: 'POST',
dataType: 'json',
data: JSON.stringify(data),
contentType = 'application/json; charset=utf-8'
});
You need to specify the datatype in the request send it to the server. The datatype attribute of your called sets the format of the response of the server, to set the datatype in the format that need to send to the server you need to use the contentType attribute

Why is my controller being called twice?

This is probably something simple, but I just can't see it.
I've got my sencha-touch application posting data to my WebService (asp.net-mvc-3). The Sencha Touch js code looks like this.
var submitCommunicateCard = function () {
console.log(rpc.views.Contact.CommunicateCard.getValues());
Ext.Ajax.request({
url: WebService('GetInTouch', 'CommunicateCard'), //http://webservice.example.com/GetInTouch/CommunicateCard
method: 'post',
params: {
callback: 'foo', //temporary until I can better setup the callback.
name: rpc.views.Contact.CommunicateCard.getValues().name,
city: rpc.views.Contact.CommunicateCard.getValues().city
}
});
};
Since I need to "thwart" my Cross Site Scripting problems, I've had to write an ActionFilter that adds the appropriate headers.
namespace WebService.Attributes
{
public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Response.Cache.SetNoStore();
HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Origin", "*");
string rqstMethod = HttpContext.Current.Request.Headers["Access-Control-Request-Method"];
if (rqstMethod == "OPTIONS" || rqstMethod == "POST")
{
HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Headers", "X-Requested-With, Accept, Access-Control-Allow-Origin");
}
}
}
}
And in my controller, I'm receiving the data from my app as follows.
[AllowCrossSiteJsonAttribute]
public JsonpResult CommunicateCard(CommunicateCardModel communicateCardModel)
{
CommunicateCardModel cc = null;
string rqstMethod = System.Web.HttpContext.Current.Request.Headers["Access-Control-Request-Method"];
if (rqstMethod != "POST")
{
// Do stuff with the model
return this.Jsonp(true);
}
else {
return this.Jsonp(false);
}
}
You'll see that I had to put if (rqstMethod != "POST") because the model from the "POST" is blank, but the model from the "OPTIONS" is not.
Here are the raw headers being passed... (note: these two headers are being passed in pairs... ie: the controller is being called twice.)
FIRST CALL
OPTIONS /GetInTouch/CommunicateCard HTTP/1.1
Host: webservice.example.com
Referer: http://192.168.3.138/
Access-Control-Request-Method: POST
Origin: http://192.168.3.138
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.71 Safari/534.24
Access-Control-Request-Headers: X-Requested-With, Content-Type
Accept: /
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
SECOND CALL (notice the very bottom line that contains the posted data (which is not contained within the first call) callback=foo&name=Chester&city=Toronto)
POST /GetInTouch/CommunicateCard HTTP/1.1
Host: webservice.example.com
Referer: http://192.168.3.138/
Content-Length: 38
Origin: http://192.168.3.138
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.71 Safari/534.24
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept: /
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
callback=foo&name=Chester&city=Toronto
Is there any way to prevent multiple calls to my controller? (or why IS my controller being called twice?)
Turns out it was fixed by making a JSONP call from my Sencha Touch app.
Ext.util.JSONP.request
// THIS IS WRONG, DON'T USE THIS CODE TO MAKE JSONP CALLS
var submitCommunicateCard = function () {
console.log(rpc.views.Contact.CommunicateCard.getValues());
Ext.Ajax.request({
url: WebService('GetInTouch', 'CommunicateCard'), //http://webservice.example.com/GetInTouch/CommunicateCard
method: 'post',
params: {
callback: 'foo', //temporary until I can better setup the callback.
name: rpc.views.Contact.CommunicateCard.getValues().name,
city: rpc.views.Contact.CommunicateCard.getValues().city
}
});
// THIS IS RIGHT
var submitCommunicateCard = function () {
console.log("Outbound Data Object:");
console.log(rpc.views.Contact.CommunicateCard.getValues());
Ext.util.JSONP.request({
url: WebService('GetInTouch', 'CommunicateCard'),
method: 'post',
callbackKey: 'callback',
params: {
name: rpc.views.Contact.CommunicateCard.getValues().name,
city: rpc.views.Contact.CommunicateCard.getValues().city
},
callback: function (result) {
console.log("Inbound Data Object:");
console.log(result);
// Handle error logic
if (result.success === true) {
Ext.Msg.alert("Sent!", "Thank you, your message has been sent!", Ext.emptyFn);
rpc.views.Contact.CommunicateCard.reset();
} else {
Ext.Msg.alert("Oops!", "looks like something went wrong, please try again.", Ext.emptyFn);
}
}
});
};

Resources