Is there a way to not just intercept but also respond to an axios request before it has been sent off? As in, send a request from the browser and respond to it from the browser + prevent it from sending request.
I know that I can use axios interceptors to intercept the request and response before it is sent and returned to the component and I know that in the request interceptor I can throw an error and trigger the response interceptor with a failed request. How can I do the same for a successful request? Given certain conditions I want axios to respond as if it passed to the server when it actually never made it past the interceptor. is this possible?
Here's pseudo code for what I've got so far:
axios.interceptors.request.use(
request => {
if (localResponse) {
throw { isLocal: true, data: { hello: 'world' } }; // <- this will stop request and trigger
// response error. I want to trigger
// the actual response callback
} else {
return request; // <- will perform full request
}
},
error => {
return Promise.reject(error);
}
);
axios.interceptors.response.use(
response => {
return response; // <- I want to trigger this callback
},
error => { // <- so far i can only trigger a response error
if (error?.isLocal) {
return Promise.resolve(error.data);
}
return Promise.reject(error);
}
);
I've tried just resolving the request interceptor but that tries to continue to fulfill the request. Does anyone have any creative ideas for solving this problem? Maybe there's a better solution than using interceptors?
Managed to compose a solution myself. This way all my axios calls will not have to be altered, i can just change behavior of interceptor.
NOTE: If anyone comes up with a better solution I will happily tag their answer as correct and upvote. For now this is the best solution I can come up with.
SOLUTION
Here's how i was able to resolve the problem
axios.interceptors.request.use(
request => {
if (localResponse) {
throw { isLocal: true, data: { hello: 'world' } }; // <- this will stop request and trigger
// response error. I want to trigger
// the actual response callback
} else {
return request; // <- will perform full request
}
},
error => {
return error?.isLocal
? Promise.resolve(error); // <- triggers response intercept
: Promise.reject(error);
}
);
axios.interceptors.response.use(
response => {
return response;
},
error => {
error?.isLocal
? Promise.resolve(error); // <- sends as successful response
: Promise.reject(error);
}
);
Essentially what I'm doing is throwing an error to prevent the request from going through, but then resolving the error instead of rejecting it. It's a little hacky but it gets the job done.
Can you just skip the request altogether in the local scenario?
function getData() {
if (localResponse) {
return Promise.resolve({ isLocal: true, data: { hello: 'world' }});
}
else {
return axios.whatever;
}
}
...
getData().then(...)
Related
I am building a react-native app, and I am starting to implement a more robust and sophisticated error-handling system, specifically for handling server errors when making http requests. Here is a basic example of how I am currently making http requests in my app.
I have a 'client.js' file which is essentially just a wrapper around axios. I have a 'get' method that looks like this:
const get = async (endpoint, config = {}) => {
try {
const result = await axios.get(domain + endpoint, config);
return result;
} catch (error) {
throw new Error(error.message);
}
};
Then, I have a file for each api endpoint that I need to access. For example, I have a 'posts.js' file, and in that file I have a 'getPosts' method:
const getPosts = async (userID, page, pageSize) => {
try {
const response = await client.get(
`${endpoint}?userID=${userID}&page=${page}&pageSize=${pageSize}`
);
return response.data;
} catch (error) {
throw new Error(error.message);
}
};
And then finally, in the component that is calling getPosts, I have a function that looks something like this:
const loadPosts = async () => {
try {
const response = await getPosts();
// do something with the response from the server
} catch (error) {
// display an error message to the client
}
}
Obviously this is a very simple example of what a request might look like, but this is the basic structure that I use throughout my app. The problem I am having is that it seems very repetitive and messy to have to wrap almost all of my functions in a try/catch block, and then basically raise an error object until I get to the function that is actually going to handle the error. Is there some sort of 'design method' for error handling that simplifies and centralizes this process? Perhaps something similar to an express-middleware when creating a node server? Or is this a standard way to handle errors in javascript?
Thank you to anyone who can help!
As you are using axios as the http library here, so you can take a look at axios interceptor in order to hook the response and do something with that before passing it to the consumer. This will help you to respond to errors raised from once cental place.
axios.interceptors.response.use((response) => {
return response;
}, function(error) {
// do what you want to do with the error.
return Promise.reject(error)
});
Or with ES5 syntax
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Not 200 Ok
// Do something with response error
return Promise.reject(error);
});
Why this url fetch isn't working?
Actually this GET method is passing some error message to be stored:
fetch('http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha', {
method: 'get'
}).then(function(response) {
}).catch(function(err) {
// Error :(
});
However if I typein same URL ( http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha ) in browser it works.
Some other places in WebExension it's working properly.
But not working in another place. Also when I enter in Firefox console too it does not work. It shows some "pending.."
This function too is showing the same behavior:
function ff_httpGetAsync(theUrl, callback, failed_cb) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
// console.log("Successfully downloaded the ajax page");
if (callback) {
if (xmlHttp.responseURL == theUrl) {
callback(xmlHttp.response);
} else {
console.log("diff response url received" + xmlHttp.responseURL);
}
}
} else {
// console.log("Got status =", xmlHttp.status);
}
}
xmlHttp.open("GET", theUrl, true); // true for asynchronous
console.log("Gettiy :" + theUrl);
xmlHttp.send(null);
}
ff_httpGetAsync('http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha', function() {
}, function() {});
I've checked the server. In this case backend pushlo90.php isn't getting called.
Not sure what is wrong with my URL?
That result tells you the promise isn't answered yet. It might work in some occasions when the promise is handled very quickly, before the page is rendered.
Using a promise you basically say 'promise me you will do this'. This promise is either resolved or rejected. Before it's resolved or rejected, it's always pending.
Adding some logging in your first function should explain.
fetch('http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha', {
method: 'get'
}).then(function(response) {
console.log(response) //do something with response data the promise gives as result
}).catch(function(err) {
console.log(err)// Error :(
});
If you don't want to use the .then(), use async/await.
const functionName = async () => {
const result = await fetch(
"http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha",
{
method: "get"
}
);
console.log(result); //this will only be done after the await section, since the function is defined as async
};
functionName();
The fetch function return a promise that when resolved returns a HTTP response. You then can access the HTTP response Example:
fetch(`https://baconipsum.com/api/?type=all-meat¶s=2&start-with-lorem=1`)
.then(response => {
// HTTP response which needs to be parsed
return response.json()
})
// accessing the json
.then(json =>console.log(json))
So the question you have to ask yourself is: what is returned from the call??
Also be aware that 404 and other HTML error codes wont lead to a reject of the promise so don't bother with catch.
For more details see https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
So, the code block that is shown in your question is -
fetch('http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha', {
method: 'get'
}).then(function(response) {
}).catch(function(err) {
// Error :(
});
So, what it says is fetch module will send a GET request to the URL provided in the request and the response or the error will go into the respective chained functions.
The error could be 404(Not found) or 401(Unauthorized) etc.
To check the error put some logging into your HTTP request handlers.
fetch('http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha', {
method: 'get'
}).then(function(response) {
console.log(`Response is {response}`)
}).catch(function(err) {
console.log(`Error is {err}`)
})
And for your other code here is the screenshot of what is getting returned from your code -
Where it clearly states 404 (Not found), hence your code will go in the error handler.
I am making a http interceptor in angular and I want to make throw error from response of $httpProvider interceptor.
As per the documentation:
response: interceptors get called with http response object. The function is free to modify the response object or create a new one. The function needs to return the response object directly, or as a promise containing the response or a new response object.
responseError: interceptor gets called when a previous interceptor
threw an error or resolved with a rejection.
I want to do the bold part in above quote so that a response with status 200 (I've a condition there) is routed to responseError where I will handle the error.
Not returning a response throws following error:
Cannot read property 'data' of undefined
I do not want to return the response but want to pass it to next handler i.e responseError.
How can I do that?
I hope I made it clear.
Thanks.
Update (Code Below):
app.config(['$httpProvider', function($httpProvider) {
interceptor.$inject = ['$q', '$rootScope'];
$httpProvider.interceptors.push(interceptor);
function interceptor($q, $rootScope) {
return {
response: response,
responseError: responseError
};
function response(response) {
if (response.data.rs == "F") {
// I want to route it responseError -->
} else {
return response;
}
}
function responseError(response) {
// I want to handle that error here
}
}
}]);
Use:
return $q.reject(response);
Also make sure you return: (Read about it here)
return response || $q.when(response);
instead of:
return response;
In my Angular app I have a login function. But when I send wrong credentials and response come with status 401: Bad Credentials (there's even response.status = 401) it still goes to success.
So I'm getting $notify "Success login and then html error page in my interceptor. That's confusing. I'm not sure what I've done to create this mess.
this.getTokenCustom = function (user) {
$http.post('/login',
JSON.stringify({username: user.username, password: user.password}))
.then(
function success(response) {
localStorage.setItem('token', response.data.token);
$.notify({message: "Success login"},{type:'success'});
$state.go('roles');
},
function error(data) {
console.log(data);
$.notify({message: data.data.message},{type:'danger'});
}
);
};
UPD
this.getTokenCustom = function (user) {
$http.post('/login',
JSON.stringify({username: user.username, password: user.password}))
.then(
function success(response) {
localStorage.setItem('token', response.data.token);
$.notify({message: response.status + " Success login"},{type:'success'});
$state.go('roles');
},
function error(data) {
console.log(data);
$.notify({message: data.data.message},{type:'danger'});
}
);
};
The likely cause of this is that you may have a custom interceptor that doesn't handle error conditions properly.
This post can point you on the right direction: http://www.jamessturtevant.com/posts/AngularJS-HTTP-Service-Success-Handler-400-Response-Code-and-Interceptors/
To quote the post:
When you handle the requestError or the responseError for a interceptor and you wish to pass the error on to the next handler you must use the promise api to reject the message:
$httpProvider.interceptors.push(function($q) {
return {
'requestError': function(rejection) {
// handle same as below
},
'responseError': function(rejection) {
if (canRecover(rejection)) {
// if you can recover then don't call q.reject()
// will go to success handlers
return responseOrNewPromise;
}
// !!Important Must use promise api's q.reject()
// to properly implement this interceptor
// or the response will go the success handler of the caller
return $q.reject(rejection);
}
};
});
A similar issue to yours was reported on Angular's Github repository and the issue had to do with a custom interceptor not handling error conditions properly.
For some reason when I try to send a POST or PUT request, the server will receive GET. Tried multiple ways of sending the request but the result is always the same.
Here is an example that demonstrates the issue
$http.post(baseUrl + '/login',{username:usr,password:psw})
.then(
function (response) {
alert(JSON.stringify(response));
return { status: "OK" };
},
function (httpError) {
alert(JSON.stringify(httpError));
if(httpError.status == "401"){
return{ status:"UNAUTH" };
}
return { status:"ERR" };
}
);
$method = $_SERVER['REQUEST_METHOD'];
always returns GET no matter what. And even the body is empty.