Need to send a POST request with a JSON body. I have to use fetch.
Original code snippet, which worked:
headers = {'Content-Type': 'application/json'};
body = {path: 'path1'};
fetch(url, {
method: 'post',
headers: headers,
body: JSON.stringify(body)
})
.then(response => {//do work});
Now I have to add Http-Only cookies for A&A.
This link has answer for that. Basically, have to add another parameter.
Afterwards updated code to:
fetch(url, {
credentials: 'include',
method: 'post',
headers: headers,
body: JSON.stringify(body)
})
.then(response => {//do work});
Server doesn't see cookie in header.
Then tested the fetch by removing everything else:
fetch(url, {
credentials: 'include',
method: 'post',
})
.then(response => {//do work});
A&A part works i.e. Server now sees cookie in header. So, added the body back and didn't believe it would work:
body = {path: 'path1'};
fetch(url, {
credentials: 'include',
method: 'post',
body: JSON.stringify(body)
})
.then(response => {//do work});
As expected, it didn't work. The Express server with CookieParser is showing that body is {}.
After added the Content-Type header:
body = {path: 'path1'};
fetch(url, {
credentials: 'include',
method: 'post',
headers: {'Content-Type': 'application/json'}
body: JSON.stringify(body)
})
.then(response => {//do work});
Now, the cookie has disappeared again. Guessing it's because of adding a new header, and replacing header generated by fetch, which includes cookie. Am I wrong?
While I was searching I found a similar question with no answer.
How should I proceed?
Related
All my GET requests are going through but POST ones fail. This happens when I update fetch-mock from 7.3.0 to 7.3.1 or later.
console.warn Unmatched POST to url
Error fetch-mock: No fallback response defined for POST to url
http.js
export const get = (url) => {
const options = {
method: 'GET',
credentials: 'same-origin'
};
return fetch(url, options).then(handleJsonResponse);
};
export const post = (url, body) => {
const headers = {
'content-type': 'application/json',
'pragma': 'no-cache',
'cache-control': 'no-cache'
};
return fetch(url, {
credentials: 'same-origin',
method: 'POST',
cache: 'no-cache',
body: JSON.stringify(body),
headers
}).then(handleJsonResponse);
};
http.spec.js
const url = '/path/to/url'
describe('get', () => {
it('makes a GET request', async () => {
fetchMock.mock({
name: 'route',
matcher: url,
method: 'GET',
credentials: 'same-origin',
response: {
status: 200,
body: []
}
});
const response = await get(url);
expect(fetchMock.called()).toEqual(true);
expect(fetchMock.calls().length).toEqual(1);
expect(fetchMock.calls('route').length).toEqual(1);
expect(response).toEqual([]);
});
});
describe('post', () => {
const requestBody = {request: 'request'};
it('makes a POST request', async () => {
fetchMock.mock({
name: 'route',
matcher: url,
method: 'POST',
credentials: 'same-origin',
cache: 'no-cache',
body: JSON.stringify(requestBody),
headers: {
'content-type': 'application/json',
'pragma': 'no-cache',
'cache-control': 'no-cache'
},
response: {
status: 200,
body: []
}
});
const response = await post(url, requestBody);
expect(fetchMock.called()).toEqual(true);
expect(fetchMock.calls().length).toEqual(1);
expect(fetchMock.calls('route').length).toEqual(1);
expect(fetchMock.lastOptions().headers).toEqual({
'content-type': 'application/json',
'pragma': 'no-cache',
'cache-control': 'no-cache'
});
expect(response).toEqual([]);
});
});
Any thoughts on what's causing this? Is there a way to get more meaningful logs to help with debugging this?
I would rather not go the alternative path of trying nock or jest-fetch-mock.
Alright, after hours of digging into the library itself I have found out where the issue was.
In my code (and the snippet above) I am stringifying the body JSON.stringify(body). The library's generate-matcher.js is parsing it JSON.parse(body) and then compares the two - the point which was causing the failure. I am now just sending it as the raw object.
In case anyone else ends up here in the future, I had the same error accompanied with fetch-mock unmatched get.
I saw the response to this issue filed to fetch-mock which prompted me to double check my expected values and mocked values.
It turns out my problem was exactly as the error described, where the mock route I was expecting and the actual route that was being called were mismatched because of a typo.
I've got a Relay setup from howtographql tutorial:
const network = Network.create((operation, variables) => {
// 4
return fetch(GRAPHQL_URL, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
credentials: 'same-origin', // <- added it to enable cookies, but it's a probably a default option anyway
body: JSON.stringify({
query: operation.text,
variables,
}),
}).then(response => {
return response.json();
});
});
I want Relay to attach a cookie to its request but it doesn't work even when I added credentials: 'same-origin'. Here's the similar issue on GitHub (even though it's more about the auth component, so this question should have a simple solution).
I know there are a few question about this topic. I applied at them but I still get this error
Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0
this code works other APIs
getData() {
fetch('http://localhost:50/data', {
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
.then((response) => { return response.json(); })
.then((result) => {
console.log(result);
});
}
php code
header('Access-Control-Allow-Origin: *');
header('Content-type:application/json;charset=utf-8');
echo json_encode(['data' => 'test']);
That unexpected < probably means you got back HTML instead of JSON. Log the response and read it, it may be an error message from your server.
Developer Mozilla has a good guide for using fetch, especially for beginners.
I actually just faced with the same problem earlier and after searching for a few hours with no luck, so I experimented on it and finally got it working.
I copy pasted the sample codes on Developer Mozilla with the complete initialized values for the object after the url
fetch(url, {
body: JSON.stringify(data), // must match 'Content-Type' header
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, same-origin, *omit
headers: {
'user-agent': 'Mozilla/4.0 MDN Example',
'content-type': 'application/json'
},
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, cors, *same-origin
redirect: 'follow', // manual, *follow, error
referrer: 'no-referrer', // *client, no-referrer
})
.then(response => response.json())
and it worked, so I tried removing each property to see if removing it will cause the error again and I found out that removing the credentials: 'same-origin will cause the error I experienced earlier.
You forgot to put a method: 'GET' on your code. Your final code should look like this. Hope it helps.
fetch('http://localhost:50/data', {
method: 'GET',
credentials: 'same-origin',
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
.then((response) => { return response.json(); })
.then((result) => {
console.log(result);
});
I am using the fetch api to get an access token returned from the github api.
When I check the network tab I see that the token is returned but I am unable to access it in my fetch request.
My code looks like this:
fetch(`https://github.com/login/oauth/access_token?client_id=***&client_secret=***&code=${code}&redirect_uri=http://localhost:3000/&state=react`, {
method: 'POST',
mode: 'no-cors',
headers: new Headers({
'Content-Type': 'application/json'
})
}).then(function(res) {
console.log(res); // I have already tried return res.json() here
})
The console displays the following error if I return res.json():
index.js:30 Uncaught (in promise) SyntaxError: Unexpected end of input
The GitHub docs states the response takes the following format:
By default, the response takes the following form:
access_token=e72e16c7e42f292c6912e7710c838347ae178b4a&token_type=bearer
I guess it isn't returning valid json but just a string so I am not sure how to access this response.
The response looks like this:
However, when I try and log out the response I get SyntaxError: Unexpected end of input
If you are using mode: 'no-cors, browser will restrict to access body. Browser has security for cross domain. If you want to access body you have to call without mode: 'no-cors property.
https://developer.mozilla.org/en-US/docs/Web/API/Request/mode
This will work
fetch(`https://jsonplaceholder.typicode.com/posts/1`, {
method: 'GET',
headers: new Headers({
'Content-Type': 'application/json'
})
})
.then(res => res.json())
.then(function(res) {
console.log(res);
})
This will not work
fetch(`https://jsonplaceholder.typicode.com/posts/1`, {
method: 'GET',
mode: 'no-cors',
headers: new Headers({
'Content-Type': 'application/json'
})
})
.then(res => res.json())
.then(function(res) {
console.log(res);
})
I think you're almost there. You've mentioned this link to the docs. If you read further, you can see that to get response in JSON, you need to include a header named Accept with the value of application/json.
fetch(` ... `, {
method: 'POST',
mode: 'no-cors',
headers: new Headers({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
}).then(function(res) {
...
})
This way, you can apply .json() on res.
I've been developing a mobile complement to my web application built with Rails. Using Fetch API, I keep getting the notice "Can't verify CSRF token authenticity".
export const login = (user) => {
fetch('http://localhost:3000/api/session', {
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({user})
})
// promise handling
}
Edit: Managed to get it to work but I still don't really understand why. If anyone has the same problem, this is how I resolved it. I managed to get the form_authenticity_token from my rails application and saved it as a variable that I then passed to the function. Haven't tested removing the credentials key.
export const login = (user, token) => {
return fetch('http://localhost:3000/api/session', {
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ user, authenticity_token: token })
})
You need to add CSRF token in headers or disable / remove CSRF on cross domain request.
export const login = (user) => {
fetch('http://localhost:3000/api/session', {
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-CSRF-TOKEN': token
},
body: JSON.stringify({user})
})
// promise handling
}
When working with Django backend you should set:
'X-CSRFToken': csrf_token // not 'X-CSRF-Token' !!!
in your JS request headers.
Environment:
To set the token on the backend side, use:
{% csrf_token %} <!-- put this in your html template -->
And then, to get the token in the JS code:
const csrf_token = document.getElementsByName('csrfmiddlewaretoken')[0].value;