Change a jquery ajax POST request into a fetch api POST - javascript

I have some json data that I have been posting to an API using $.ajax but I would like to update this to use the fetch API. However I seem to have it setup the Fetch API request ends up returning a 403 so I must be missing something but I can't work it out.
Ajax request:
$.ajax({
type: 'POST',
url: url,
data: {
'title': data.title,
'body': data.body,
'csrfmiddlewaretoken': csrf_token,
'request_json': true
},
success: function (data) {
console.log(data)
}
});
Fetch attempt (one of many):
let payload = {
'title': data.title,
'body': data.body,
'csrfmiddlewaretoken': csrf_token,
'request_json': true
}
let request = new Request(url, {
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: JSON.stringify( payload )
});
fetch(request)
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
})
.then((response) => response.json())
I have tried with various different headers, content encoding and sending the data as form data using:
let form_data = new FormData();
form_data.append( "json", JSON.stringify( payload ) );
let request = new Request(url, {
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: form_data
});
...
Any help would be great and if you need any more info let me know
Thanks

To port an existing jQuery.ajax request to fetch, you need to consider that jQuery always includes cookies for you, but fetch does not.
Quoting MDN (emphasis mine):
Note that the fetch specification differs from jQuery.ajax() in mainly two ways that bear keeping in mind:
- The Promise returned from fetch() won’t reject on HTTP error status [ ... ]
- By default, fetch won't send or receive any cookies from the server, resulting in unauthenticated requests if the site relies on maintaining a user session (to send cookies, the credentials header must be sent).
Edit: spec has changed since then, so this should no longer be a problem:
Since Aug 25, 2017. The spec changed the default credentials policy to same-origin. Firefox changed since 61.0b13.
So the following (returning to original answer) only applies to "older" browsers.
Thanks David Richmond from comments :)
So you get 403 (Forbidden) because your API likely relies on cookies for authentication/authorization (even in your case, where you send a csrfmiddlewaretoken, the server-side framework might still expect a cookie with that -- guessing Django?).
To fix this, add credentials: "same-origin" to your Request (*), like so:
let request = new Request(url, {
method: 'post',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
(*) Valid options for credentials are:
omit: Never send cookies. This is the default (and your problem).
same-origin: Only send cookies if the URL is on the same origin as the calling script.
include: Always send cookies, even for cross-origin calls.

You say:
'Content-Type': 'application/x-www-form-urlencoded'
and
body: JSON.stringify( payload )
JSON Encoding is not the same thing as WWW Form Encoding!
You also tried
form_data.append( "json", JSON.stringify( payload ) );
FormData objects are converted to Multipart MIME.
Multipart MIME is also not the same as WWW Form Encoded data.
JSON nested inside Multipart MIME even less so.
This question describes how to convert an object (payload) into a Form Encoded string.

Related

Sending a POST request through Javascript on Django - failed to retrieve data

I'm trying to send a POST request to my Django view using plain javascript (I don't want any unnecessary libraries involved). The data is not sent through a form, but by using fetch. For now I just want to be able to manipulate the request.POSTin my views.py, nothing more.
Here's my code:
Javascript
let article = document.querySelector('article')
articleId = article.getAttribute('data-product-id')
# some other stuff
fetch("{% url 'shop:shoplist' 1 %}", {
method: 'POST',
dataType: "application/json",
data: {'article_id': articleId},
headers: {'X-CSRFToken': csrf_token}
})
Python
if request.method == 'POST':
testing = request.POST
return JsonResponse({'test': testing})
The request is sent, the csrftoken is received correctly, but the request.POST returns just <QueryDict: {}>, instead of what I'm expecting (headers, data...).
I've searched and found a lot of similar questions, the most similar one being this one, but still I can't seem to find a solution.
Any idea?
Try to add 'content-type' to headers in fetch call (instead of dataType parameter) and change data parameter to body with stringified object:
fetch("{% url 'shop:shoplist' 1 %}", {
method: 'POST',
body: JSON.stringify({'article_id': articleId}),
headers: {
'X-CSRFToken': csrf_token,
'Content-Type': 'application/json'
}})
The request.POST contains only the parameters that are form encoded. Since your data type is application/json they are empty. The request.body contains the actual json data.

Fetch API call causes new Asp.net session

I'm in the process of removing jQuery in one of my asp.net mvc projects in favor of using straight vanilla JS. Now that I've replaced $.ajax POST calls with Fetch API calls, each call triggers a new session on the server.
This has been driving me up the wall for the past few days, and I've narrowed it down to specifically this switch from using jQuery Ajax to Fetch API. My new Fetch API calls work perfectly otherwise, still performing the needed server-side work. The just trigger a new server session once they return.
Obviously, this is a major issue, as my user session data keeps getting reset. Any idea as to why this happens? Or anyone know of any workarounds, while not having to revert back to using jQuery?
My previous 'jQuery'-based POST call:
Post(route, data) {
$.ajax({
type: 'POST',
url: route,
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8"
}).done((result, statusText, jqXHR) => {
return result;
});
}
My new 'Fetch API'-based call:
async Post(route, data) {
let response = await fetch(route, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-type': 'application/json'
},
body: JSON.stringify(data)
});
let result = await response.json();
return result;
}
In my Global.asax.cs:
protected void Session_Start(object o, EventArgs e) {
Debug.WriteLine("Session_Start");
HttpContext.Current.Session.Add("__MySessionData", new MySessionDataClass());
}
As I mentioned above, the Fetch API call works perfectly fine other than resetting my session, which I know from the Debug.WriteLine call. The jQuery Ajax call also works perfectly fine, and does not trigger a new session, however I'm trying to remove my dependency on jQuery.
Thoughts?
You're not passing in the ASP.NET_SessionId cookie with your custom request.
You are using fetch. By default it uses omit for the credentials. This means, as said on the MDN page:
By default, fetch won't send or receive any cookies from the server, resulting in unauthenticated requests if the site relies on maintaining a user session (to send cookies, the credentials init option must be set).
JQuery does send cookies, but only those on the same domain.
AJAX calls only send Cookies if the url you're calling is on the same domain as your calling script.
Source
To fix this, you need to tell fetch to send cookies. From this post:
fetch('/something', { credentials: 'same-origin' }) // or 'include'
Figured out the issue with the help of #gunr2171, so self-answering in case anyone else comes across a similar issue.
Turns out that my new Fetch API call was not sending the current Asp.net session cookie with the request, so the server would start a new session thinking that one didn't exist yet. Tweaking the Fetch API call to include credentials: 'include' in the options allows it to send the current session cookie and the server will no longer create a new one after every call.
For reference, my new Fetch API call now looks like:
async Post(route, data) {
let response = await fetch(route, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-type': 'application/json'
},
credentials: 'include',
body: JSON.stringify(data)
});
let result = await response.json();
return result;
}

fetch method not posting as expected, compared to jQuery ajax post method

I am eager to use the new fetch method and I understand it can be used for all http method calls to a Rest endpoint.
So I have the following very simple API endpoint which I know works, get and post requests work. The issue I am having is getting the post request to work on the fetch method.
My code for the fetch method is
var ingredient = {
'name': 'Salt',
}; // a JSON object used as the body for the post request
fetch('http://pantler-180711.nitrousapp.com/api/v1/Ingredients',
{
method: "POST",
body: JSON.stringify( ingredient )
})
.then(function(res){ console.log( 'Data posted') })
I then get the following error message. 422 (Unprocessable Entity)
If on the other hand I do something very similar but this time using the classic jQuery ajax post method it works.
$.ajax({
url: 'http://pantler-180711.nitrousapp.com/api/v1/Ingredients',
type: 'POST',
data: 'name=Salt', // or $('#myform').serializeArray()
success: function() { console.log('Data posted'); }
});
Any help here would be appreciated, it feels like I am missing something small here, and documentation on fetch method is scant on the web.
The two request sends two different post bodies one is application/x-www-form-urlencoded (jQuery) and the other is application/json.
You'll have to change the fetch call to send the same type of data as the $.ajax call.
You may have to explicitly set the content type in the fetch request.
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
var searchParams = new URLSearchParams();
searchParams.append("name", "Salt");
fetch('http://pantler-180711.nitrousapp.com/api/v1/Ingredients',
{
method: "POST",
headers: myHeaders,
body: searchParams
})
.then(function(res){ console.log( 'Data posted') })

How to destroy the response before each http request using angular js?

I want to clear my http response before each request sending to API.
My Http request:
$http(
{
method: 'GET',
url: URI,
headers: { 'Content-Type': 'application/json' },
async: false
}).success(function (data) {
$('#processing').hide();
$scope.items = JSON.parse( angular.toJson(data));
$scope.header = $scope.items[0];
Could you please help me ,so that each time I will get new response?
Thanks
The response is probably cached by browser or something between your browser and server.
You can either try to prevent caching by adding header which tells the browser not the cache given response
Or
you can append unique timestamp to the URL, like this
var no_cache_url = URL + new Date().getTime()
If you are using some query string in your url then the code is slightly more complicated, but you will figure that out.

AngularJS can't send post request with Content-Type:application/json

Currently I am using angularJS and CoffeeScript to try to send a post request, and my sample code is:
login: (user, callback)=>
baseUrl = 'http://localhost:3000/api/v1/sessions'
#$http({
method: 'POST',
url: baseUrl,
data: user
}).success (result)->
callback(result)
But when I call it, it just send 'OPTIONS' request instead of POST request.
And the request detail is:
If I add header to this method,
login: (user, callback)=>
baseUrl = 'http://localhost:3000/api/v1/sessions'
#$http({
method: 'POST',
url: baseUrl,
data: user,
headers:
'Content-Type': 'application/json'
}).success (result)->
callback(result)
It still doesn't works, but if I change headers to 'Content-Type': 'application/x-www-form-urlencoded', then it can send post requests.
But the request Content-type is not not I want.
I also try to modify the request data to JSON by: data: JSON.stringify(user), but still not working.
UPDATES
Guys, I did another spike on this issue. Which is I am jquery to send the request and it works fine, but I found an wired thing that is they have different request data.
Jquery
$.ajax({
type: "POST",
url: "http://localhost:3000/api/v1/sessions",
data: {
"user":{
"email":"wahxxx#gmail.com",
"password":"123456"
}
},
success: function(){
}
Screenshot for Jquery
Angular
login: (user, callback)=>
baseUrl = 'http://localhost:3000/api/v1/sessions'
#$http({
method: 'POST',
url: baseUrl,
data: {
"user":{
"email":"wahxxx#gmail.com",
"password":"123456"
}
},
headers:
'Content-Type': 'application/x-www-form-urlencoded'
}).success (result)->
callback(result)
Now it can send request,but I just got 401 when trying to do request.
Screenshot for Angular
So I think the issue may due to the format of the angular request data.
You are hitting CORS restrictions and same origin policy.
Easiest solution is to deploy web frontend and the api together as one app. If ports are different even on the same machine then one needs to deal with same origin policy.
Options is a preflight query. Make your backend accept it and it should be fine.
More reading:
http://www.html5rocks.com/en/tutorials/cors/

Categories