I got this kind of response from fetch when I inspect it in the console, as shown in the picture. However, when i check from the devtool Network, it shows the correct error response. Any idea on how to deal with this?
export function loginRequest(data){
return (dispatch, getState) => {
let tmp = Object.assign({},data)
var request = new Request('https://aaa.com/json', {
method: 'POST',
mode: 'cors',
headers: new Headers({
'Content-Type': 'text/plain'
})
});
fetch(request).then((res)=>{
alert(JSON.stringify(res))
dispatch({
type: types.LOGIN,
data: res
})
}).catch(err =>{
alert(JSON.stringify(err))
alert(err.errMsg)
dispatch({
type: types.LOGIN,
data: data
})
console.log(JSON.stringify(err))
})
}
}
You need to parse the response to JSON to see the response that matches what you see in the network tab.
fetch('//offline-news-api.herokuapp.com/stories')
// First you can deal with the response, checking status code, headers, etc.
.then(function(response) {
if (response.status >= 400) {
throw new Error("Bad response from server");
}
// This is the line you are missing
return response.json();
})
// Now you will see the proper JSON response that should match
// what you see in the network tab.
.then(function(stories) {
console.log(stories);
});
When using fetch, you can see the raw response, which is what your screenshot seems to be, and then you can parse it to JSON to see the body of the response as you are expecting. This allows more fine-tuned control of error handling, responses, etc.
You can also use axios where you don't have to parse your response and don't have to manually reject if there's 400 errors, it will go to catch instead.
Related
I have a JavaScript that makes a Fetch post call to the backend of the site. If the post-call goes well, the Fetch is able to handle the response. If something goes wrong in the post-call, the Fetch is not able to handle the error.
This are my codes:
async function startFetch(url, data_post){
return fetch(url, {
method: 'POST', // or 'PUT'
body: JSON.stringify(data_post), // data can be `string` or {object}!
headers:{
'Content-Type': 'application/json'
}
})
.then(response => response.json());
}
async function makeFetch(arrRows){
let url = 'somebackendpost';
for(const item of ArrRows){
let data_post = {};
data.name = item;
await startFetch(url, data_post)
.then(data => {
//when the fetch is okay, this part is executed
console.log(data);
//do other things like output to html
$('#result').append('<p>' + data.result + '</p>');
})
.catch(error ={
//a timeout 504 error occured, but this part seemed not to execute
console.log('error is', error);
//the code below is wrong (but has since been removed then)
//Is this the reason why the catch error part did not work?
//Since the code below has been removed, can I expect the catch error to now work?
$('#result').append('<p>' + data.result + '</p>');
});
}
}
function startProcess(){
//called by button click
let arrRows = ["Row1", "Row2", "Row3"];
makeFetch(arrRows);
}
At the time, the code was executed, there was a server issue. The browser console displayed a Gateway timeout error 504. Here is the console logs:
Fetch failed loading: POST "mysite/somebackendpost"
POST mysite/somebackendpost 504 (GATEWAY TIMEOUT)
error is SyntaxError: Unexpected end of JSON input
at fetch.then.response
Uncaught (in promise) ReferenceError: X is not defined
at startFetch.then.catch.error
Try updating your startFetch method to first check that the fetch response is "ok" before attempting to parse the response as json. This will catch most error scenarios (that are currently going undetected), before you attempt to parse json.
So, an update as follows should allow you to respond to errors correctly:
async function startFetch(url, data_post){
return fetch(url, {
method: 'POST', // or 'PUT'
body: JSON.stringify(data_post), // data can be `string` or {object}!
headers:{
'Content-Type': 'application/json'
}
})
.then(response => {
// Check that the response is valid and reject an error
// response to prevent subsequent attempt to parse json
if(!response.ok) {
return Promise.reject('Response not ok with status ' + response.status);
}
return response;
})
.then(response => response.json());
}
Hope this helps!
Below code is making ajax request and getting data. As you can see then((response) => response.json()) is to convert response to json. But what if the http status is unauthorized or something else (400, 401 etc) and no json data is returned. Is there any better way to check status code before I convert the response to json?
I guess the catch is where I can do this, however, I want to keep catch for unhandled exceptions. Based on the status code I would like to show exact error user received(of course not for login component for security reasons). For example, Invalid Authorization, Invalid token.
My fetch code looks like this:
onLoginPressed() {
fetch('http://localhost:8080/api/user/login', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: this.state.username,
password: this.state.password
})
}).then((response) => response.json())
.then((responseJson) => {
if (responseJson.status === 500) {
Alert.alert(responseJson.message);
} else if(responseJson.profileCompleted == false){
this.props.navigation.navigate('SetupOne');
} else {
this.props.navigation.navigate('Home');
}
}).catch((error) => {
console.error(error);
});
}
If you look at the docs for fetch, you will see this:
The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will resolve normally (with ok status set to false), and it will only reject on network failure or if anything prevented the request from completing.
And further down:
An accurate check for a successful fetch() would include checking that the promise resolved, then checking that the Response.ok property has a value of true.
So in other words, if this concerns you, you might want to check the status first, before doing your json body conversion. Something like the following:
const handleResponse = res => {
if(res.ok) {
return res.json()
}
throw new Error('Network response was not ok.')
}
fetch(someUrl, details)
.then(handleResponse)
.then(doYourWorkWithJSON)
.catch(err => console.error(err))
I am sending a status code 422 from my backend code with response body which contains the description of the error. I am using axios post as below to post a request:
post: function(url, reqBody) {
const request = axios({
baseURL: config.apiUrl,
url: url,
headers: {
'Content-Type': 'application/json',
'Authorization': sessionStorage.getItem('token')
},
method: 'POST',
data: reqBody,
responseType: 'json'
});
return request
.then((res) => {
return res;
})
.catch((error) => {
console.log(error);
return error;
})
}
The problem is when backend is returning error code 422, the error object I am catching has no information about response body. Is there any way I can retrieve the error text?
I had this same issue and the answer (as per Axios >= 0.13) is to specifically check error.response.data:
axios({
...
}).then((response) => {
....
}).catch((error) => {
if( error.response ){
console.log(error.response.data); // => the response payload
}
});
See here for more details.
The "body" of an AXIOS error response depends from the type of response the request had.
If you would like full details about this issue you can see this blogpost: How to catch the body of an error in AXIOS.
In summary AXIOS will return 3 different body depending from the error:
Wrong request, we have actually done something wrong in our request (missing argument, bad format), that is has not actually been sent. When this happen, we can access the information using error.message.
axios.get('wrongSetup')
.then((response) => {})
.catch((error) => {
console.log(error.message);
})
Bad Network request: This happen when the server we are trying to reach does not respond at all. This can either be due to the server being down, or the URL being wrong.
In this case, we can access the information of the request using error.request.
axios.get('network error')
.then((response) => {})
.catch((error) => {
console.log(error.request );
});
Error status: This is the most common of the request. This can happen with any request that returns with a status that is different than 200. It can be unauthorised, not found, internal error and more. When this error happen, we are able to grasp the information of the request by accessing the parameter specified in the snippets below. For the data (as asked above) we need to access the error.response.data.
axios.get('errorStatus')
.then((response) => {})
.catch((error) => {
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
})
For those using await/async and Typescript
try {
const response = await axios.post(url, body)
} catch (error) {
console.log(error.response.data);
}
For react native it just worked for me
api.METHOD('endPonit', body)
.then(response => {
//...
})
.catch (error => {
const errorMessage = JSON.parse(error.request.response)
console.log(errorMessage.message)
})
We can check error.response.data as #JoeTidee said. But in cases response payload is blob type? You can get error response body with the below code.
axios({
...
}).then((response) => {
....
}).catch(async (error) => {
const response = error.response
if(typeof response.data.text === function){
console.log(await response.data.text()); // => the response payload
} else {
console.log(response.data)
}
});
I am returning a string from backend but expecting a json as response type. So I need to return an object instead of string for axios to process it properly.
In my case I wanted to retrieve a response 404 error message (body).
I got body with error.response.data but I couldn't display it because the type was ArrayBuffer.
Solution:
axios.get(url, { responseType: 'arraybuffer' }).then(
response => {...},
error => {
const decoder = new TextDecoder()
console.log(decoder.decode(error.response.data))
}
)
Related posts:
Converting between strings and ArrayBuffers
I'm trying to create a scheme to intercept and handle requests from an API middleware, however, for whatever reason I'm unable to properly handle non JSON responses from my API endpoint. The following snippet works just fine for server responses formatted in JSON however say an user has an invalid token, the server returns a simple Unauthorized Access response that I'm unable to handle even though I am supplying an error callback to the json() promise. The Unauthorized Access response message is lost in the following scheme.
const callAPI = () => { fetch('http://127.0.0.1:5000/auth/', {
method: 'GET',
headers: {
'credentials': 'include',
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Basic bXlKaGJHY2lPaUpJVXpJMU5pSXNJbVY0Y0NJNk1UUTVPRE15TVRNeU5pd2lhV0YwSWpveE5EazRNak0wT1RJMmZRLmV5SnBaQ0k2TVgwLllFdWdKNF9YM0NlWlcyR2l0SGtOZGdTNkpsRDhyRE9vZ2lkNGVvaVhiMEU6'
}
});
};
return callAPI().then(res => {
return res.json().then(responseJSON => {
if(responseJSON.status === 200){
return dispatch({
type: type[1],
data: responseJSON,
message: success
});
} else if(responseJSON.status === 401) {
return dispatch({
type: type[2],
message: responseJSON.message
});
}
return Promise.resolve(json);
}, (err) => {
console.log(err.toString(), ' an error occured');
});
}, err => {
console.log('An error occured. Please try again.');
});
Try using text method of Body: res.text().
Try to wrap your response handling code in a try...catch block like this:
return callAPI().then(res => {
try {
return res.json().then(responseJSON => {
[...]
catch(e) {
console.error(e);
}
});
Body.json() throws when the body is actually not JSON. Therefore, you should check if the body contains JSON before you call json() on it. See https://developer.mozilla.org/en-US/docs/Web/API/Response.
I have the following code for making POST Requests.
I'm not 100% sure about error handling here, but it was important for me that I get body text when request is not successful.
One issue that I still do have is - if server responds with 200 OK but invalid json - can I log that payload?
What would be the correct way of logging for Fetch?
Fetch(data.notificationUrl, {
method: 'POST',
body: post_data,
headers: {
'Content-Type': 'application/json'
}
}).then((res) => {
if (!res.ok) {
// Could reject the promise here but than response text wouldn't be available
//return Promise.reject(`Response was not OK. Status code: ${res.status} text: ${res.statusText}`);
return res.text().then((txt) => `Response was not OK. Status code: ${res.status} text: ${res.statusText}.\nResponse: ${txt}`);
}
// response ok so we should return json, could follow https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch and determine the payload type by content-type header...
return res.json();
}).then((response) => {
if (response) {
// set result
// ...
// redirect
return reply.redirect(data.redirectUrlDirectory);
}
return reply(Boom.preconditionFailed(`Did not reply with correct payload! json:'${JSON.stringify(response)}'`));
}).catch((err) => {
return reply(Boom.badData(`Could not notify on url ${data.notificationUrl} about the payment ${id}.\nError: "${err}"`));
});
I would use something like this.
This fist option asumes your service response always the header "application/json" and a the pay load simple text which I mock it like this.
var app = new express();
app.get('/valid', function(req, res){
res.json({ok: "ok"});
});
app.get('/invalid', function(req, res){
res.json("bad json body");
});
and the fetch json handling should looks like this. The other part of your code looks like good for me.
var response2 = res.clone();
return res.json().then((json) => {
// log your good payload
try {
// here we check json is not an object
return typeof json === 'object' ? json : JSON.parse(json);
} catch(error) {
// this drives you the Promise catch
throw error;
}
}).catch(function(error) {
return response2.text().then((txt) => `Response was not OK. Status code: ${response2.status} text: ${response2.statusText}.\nResponse: ${txt}`);
//this error will be capture by your last .catch()
});
xxx.clone() allows you to resolve multiple times the same response and create your own combinations like the previous one.