Handling 401 in axios when using both interceptors and transformResponse - javascript

I have a method for fetching data from server, basically just another GET request. However I use transformResponse in order to modify data to suit my needs.
Also I have interceptor for dealing with 401 error responses.
The thing is, when server sends 401 response on my GET request, transformResponse code runs and tries to modify server response, which is obviously is not what transformResponse expected. This leads to the situation when interceptor receives javascript error and doesn't get full http response (error.response returns undefined).
Here is two code samples.
Getting data from server:
findOak() {
console.log('find oak')
return axios.get(`${baseURL}/`, {
params: {
is_oak: true
},
transformResponse: (response) => {
console.log('transformResponse')
return companyTransformer(response.data[0])
}
}).then(response => response.data)
}
Setting up interceptors:
axios.interceptors.response.use(
response => response,
error => {
console.log('interceptor')
console.log(error.response)
if (error.response && error.response.status === 401 && authenticated()) {
logout()
window.location.href = '/'
} else {
return Promise.reject(error)
}
}
)
So the error.response is undefined, instead of server response in holds the error: Uncaught (in promise) TypeError: Cannot read property '0' of undefined which happens in transformResponse.
How to deal with this case?

Related

Trying to access error response 500 axios

I am not able to access the response of error - 500 in axios
export const dowloadFilePDF = (data) => {
return axios
.request({
method: 'GET',
url: `${basePath + data[0]}`,
responseType: 'blob',
headers: { Authorization: Authorization },
})
.then(response => {
console.log(response)
let fileName = response.headers['content-disposition']?.split(';')[1]?.split('=')[1]?.split('"').join('')
fileName = fileName ? fileName : 'data.pdf'
fileDownload(response.data, fileName)
})
.catch((error) => {
console.log(error.response.data)
})
}
I am not getting the response instead its returning as
data : Blob {size: 215, type: 'application/json'}
According to the documentation, you can't assume error.response will be filled in. Here's the code the documentation shows with the inline comments explaining it:
Handling Errors
axios.get('/user/12345')
.catch(function (error) {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
console.log(error.config);
});
There's another aspect to this as well: You're calling catch on the promise returned by then, not on the promise returned by axios. If the axios promise is rejected, you'll reach that rejection handler, but you'll also reach it if the axios promise is fulfilled but then your fulfillment handler throws an error (or returns a promise it ultimately rejects). In that latter case, the error probably won't have a response property at all.
the best way to catch errors instead of trying a lot of lines of code in the catch method by promise is using the tools in the Axios names interceptor.
interceptor has two property request and response. In response we can simulate the errors status and based on the status code we can do whatever we want. for example :
axios.interceptors.response.use(null, error => {
console.log("error : " , error);
const expectedError = error.response && error.response.status >= 400 &&
error.response.status < 500;
if (expectedError) {
return Promise.reject(error);
}
alert("unexpected error is happen");
});
if you need more help here is the original link

Avoid handling if error has response in Vue with Axios in every request

I use the axios interceptors to handle some errors, specially the errors without response. And in some parts of my project, I use the message contained in the error.response.data for validations and showing a messaged stored in the backend. But this interceptor is not preventing me from having to check if the error has a response.
My interceptor:
axios.interceptors.response.use(
function (response) {
...
},
function (error) {
if (!error.response) {
...
return Promise.reject(new Error(error.message))
}
An example of a request that depends on having the error.response:
this.$store.dispatch('updateField', { [this.fieldKey]: this.value ? this.value : null }).catch((error) => {
this.validateField(error.response.data)
})
But I'd have to put the validateField call inside an if(eror.response) to avoid an error in the console, and spread this if all over my code?
response can be treated as optional, because it actually is:
this.validateField(error.response?.data)
Or normalized error that contains needed properties and doesn't rely on the structure of Axios error can be created in an interceptor:
function (rawError) {
const error = new Error(error.response?.data?.myError || error.message);
error.data = error.response?.data || null;
error.headers = error.response?.headers || null;
error.status = error.response?.status || 0;
return Promise.reject(error);
}

I'm getting "Uncaught (in promise) TypeError: Cannot read property 'data' of undefined" error in axios patch api

I am trying to update an array of objects using an Axios/Node.js API. I have successfully created the array, but when I try to pass in through an axios patch request, I am getting the "Cannot read property 'data' of undefined". Anyone know what I might be doing wrong?
My axios function is below:
export const updateTrans = async (transArr) => {
try {
const res = await axios({
method: 'PATCH',
url: `http://127.0.0.1:7000/api/v1/property/${id}`,
data: {
"transactions": transArr }
});
if (res.data.status === 'success') {
showAlert('success', 'Data updated successfully!');
}
} catch (err) {
console.log(err.response.data.message );
showAlert('error', err.response.data.message);
}
};
I have tried to stringify "transArr" as well, but still no luck.
The problem is that you're accessing err.response.data without checking if err.response exists. err.response is only present if the request was made and there was a response. If, for example, a CORS error or some network error occurred (or some other error inside the try block), it will not be present.
Just check that err.response exists before using it.
try {
// ...
} catch (err) {
console.log(err, err.response && err.response.data);
if (err.response && err.response.data) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
showAlert("error", err.response.data.message);
return;
}
showAlert("error", err.message);
}

Axios post entering catch block even after successful Rest API call completion

My axios post request is not returning the value returned by the API in a non success 401 scenario. It works fine when its a success scenario.
My reset Password API returns the status code, and a message for every call. when I use post man to test its output for resetting a password, giving incorrect current password, I get
Postman Output:
statusCode: 401,
headers: {
.......
........
},
body: "{"code":"NotAuthorizedException","name":"NotAuthorizedException","message":"Incorrect username or password."}" <--- This is the body output
But in my axios post, it goes to the catch block:
await Axios.post(resetAPIUrl, resetParams, config).then(value=> {
console.log(`Returned data ----> ${JSON.stringify(value)}`);
resolve(value);
}).catch(error=>{
console.log(`Failing--->${error}`)
reject(error)
});
this is the error that I am getting in the catch block of Axios post:
Error: Request failed with status code 401
The error reason is correct. But why isn it going into catch block? Its a successful completion of the process. Calling the API directly from post man gives me the correct output structure.
Interceptor was the answer. Since Axios doesn't consider any response other than 200 as the success scenario, interceptors can be used to capture the other responses:
{
Axios.interceptors.request.use(req=>{
console.log(`This is the request ---> ${req.method} ${req.url}`)
return req;
})
Axios.interceptors.response.use(res => {
console.log(`res status ---> ${res.status}`)
resolve(res)
return res;
}, (error)=>{
console.log(`This is the error status ---> ${error.response.status}`)
if(error.response.status === 401){
resolve(error.response);
}
})
await Axios.post(resetAPIUrl, resetParams, config);

whatwg-fetch response.ok === true even if server is offline

I make requests using whatwg-fetch and I'm trying to check response.status === 0 in case response.ok is false to see if the server is currently offline and notify users accordingly.
Problem is, response.status is always 200 and response.ok is always true when the server's offline.
const interpretJsonResponse = (response, callback):object => {
const responseClone = response.clone();
if (response.ok) {
response.json().then((json) => callback(null, respondJson(json))).catch(callback);
} else {
// Response is not ok. Callback with a generic error to show to the user
// and log the error for detailed info
if (response.status === 0) {
// Backend server is not up
callback('Backend server is not responding. Please retry later!');
} else {
callback(`Error in request: ${response.statusText}`);
}
}
// Return a clone to allow usage of .json() again if necessary
return responseClone;
};
I'm also getting a JSON parse error since the response is empty, given the lack of servers to reply. What am I / could I be missing?
Example Response object:
"url":"http://localhost:8000/client_v1/auth/",
"status":200,
"statusText":"OK",
"headers": {}
"ok":true,
"body":{
"_readableState":{
"highWaterMark":16384,
"buffer":[],
"length":0,
"pipes":null,
"pipesCount":0,
"flowing":null,
"ended":false,
"endEmitted":false,
"reading":false,
"sync":false,
"needReadable":true,
"emittedReadable":false,
"readableListening":false,
"objectMode":false,
"defaultEncoding":"utf8",
"ranOut":false,
"awaitDrain":0,
"readingMore":false,
"decoder":null,
"encoding":null
},
"readable":true,
"_events":{},
"_writableState":{
"highWaterMark":16384,
"objectMode":false,
"needDrain":false,
"ending":false,
"ended":false,
"finished":false,
"decodeStrings":true,
"defaultEncoding":"utf8",
"length":0,
"writing":false,
"corked":0,
"sync":true,
"bufferProcessing":false,
"writecb":null,
"writelen":0,
"buffer":[
],
"pendingcb":0,
"prefinished":false,
"errorEmitted":false
},
"writable":true,
"allowHalfOpen":true,
"_transformState":{
"needTransform":false,
"transforming":false,
"writecb":null,
"writechunk":null
}
},
"bodyUsed":false,
"size":0,
"timeout":0,
"_raw":[],
"_abort":false
EDIT: Added full function
EDIT #2: Added example whatwg-fetch Response object
I just found this caveat, in fetch, which could be useful to you:
Caveats
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 even if the response is a HTTP 404 or 500. Instead, it will resolve normally, and it will only reject on network failure, or if anything prevented the request from completing.
So, I should simply check the response for emptyness (if response is empty, as you say):
if (response === null) {
return callback('Empty reponse from backend server. Please retry later!');
}
if (response.ok) {
...

Categories

Resources