Not getting http error codes from rest calls - javascript

I need to be able to differentiate between a 400 and 500 range error code for different flows in our application.
Lets say I have 3 rest calls,
First will return 200,
second will return 401,
and third will return 502
I started using aurelia-http-client, which is where I first noticed that I was getting the http error code = 0 in the promise`s reject callback.
Update: Using aurelia-fetch-client returns only a string as a error response, thus wasn't an option either.
I then tried using an ajax call and a basicXMLHttpRequest, which yielded the same result. For 200 range, I got the codes, but anything above, I received a statusCode of 0.
Update: I am running Version 63.0.3239.132 of Chrome if it makes a difference.
What I've tried:
I've tried about 5 different variations for fetch.
fetch(url, {
method: requestMessage.method,
headers,
body: JSON.stringify(content)
})
.then((result) => {
resolve(result)
})
.catch((error) => {
reject(error);
});
Outputs a string error
Using aurelia-http-client
this.httpClient.createRequest(url)
.asPut()
.withContent(params)
.send()
.then((response) => {
resolve(response);
},
(error) => {
reject(error);
});
- StatusCode is always 0 for errors
Also (This just builds up a dynamic XmlHttpRequest):
private retryRequest(): void {
var xhr = this.setupXhr();
xhr.onreadystatechange = () => this.stateChange(xhr);
setTimeout(() => {
xhr.send(JSON.stringify(this.content));
}, 1000);
}
private setupXhr(): XMLHttpRequest {
var xhr = new XMLHttpRequest();
xhr.open(this.method, this.url, true);
xhr = this.addHeaders(xhr);
return xhr;
}
private addHeaders(xhr: XMLHttpRequest): XMLHttpRequest {
for (let key in this.headers) {
if (this.headers.hasOwnProperty(key)) {
xhr.setRequestHeader(this.headers[key].key, this.headers[key].value);
}
}
return xhr;
}
private stateChange(xhr: XMLHttpRequest): void {
logger.debug(' ::>> xhr = ', xhr);
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 400) {
this.resolve(JSON.parse(xhr.response));
} else if (xhr.status >= 500) {
this.retryRequest();
} else {
// this.retryRequest();
this.reject(xhr.response); // call after a # of fails for this ???
}
}
}
Which only returns the 200 range http status codes
Also:
$.ajax({
type: requestMessage.method,
url,
data: JSON.stringify(content),
headers,
success: (data) => {
logger.debug(' ::>> rest call was a success ', data);
resolve(data);
},
statusCode: {
502: (jqXHR) => {
logger.debug(' ::>> received 502 ');
var retryAfter = jqXHR.getResponseHeader('Retry-After');
retryAfter = parseInt(retryAfter, 10);
if (!retryAfter) { retryAfter = 5 };
setTimeout(query, retryAfter * 1000);
}
}
});
- Which never gets to the 502 callback. I've tried other status codes as well
Is there a way of getting the error codes which I might be missing? Any help appreciated

I had similar requirement and have implemented this using following code
$.ajax({
type: "POST",
url: urlAjax,
dataType: 'json',
headers: headerValue,
data: _dataValue,
crossDomain: true,
beforeSend: function () {
//any operation
},
complete: function () {
// any after operation
},
success: function (data) {
// All 2XX will reach here
},
error: function (jqXHR, textStatus, error) {
var Check = JSON.parse(jqXHR['responseText']);
// In above array you will get whole error response
}
}).done(function (rs, textStatus, xhr) {
// on finished
});
In jqXHR, textstatus and errror params you will receive all the information about errors and codes related to it.
Hope that helps.

Found the issue:
I initially thought Harshal Bulsara's was what resolved my issue. It is a nice implementation, but I found the actual issue.
I found that our nginx response didn't send the required headers.
By adding the Access-Control-Allow-Origin header, I received the statusCode.

Related

Interceptor for fetch and fetch retry? (Javascript)

I am trying to create an interceptor for fetch in javascript (React to be more specific). It should get the result from every fetch that gets called, and if it is an 401 error it should initiate a new fetch call to another route to get a cookie (a refresh token). Then, the original fetch call should be tried again (because now the user is logged in).
I have managed to trigger the new fetch call and send back the cookie for each, but I got these two problems below:
I do not now how to retry the fetch call after the refresh token has been recieved. Is that possible? I found the fetch-retry npm (https://www.npmjs.com/package/fetch-retry) but not sure how and if I can implement that on an interceptor when it should be done for the original fetch call.
I seem to be doing something wrong with async await (I think), because the intercept is not waiting for the fetch call before returning the data (the statuscode on the original fetch seems to be 401 and not 200 which it should be after we get the cookie. I also tried to return the response of the fetch inside the interceptor but that returned undefined).
Any idea about how to solve this? Anyone who have done something similar?
Below is my code:
(function () {
const originalFetch = fetch;
fetch = function() {
return originalFetch.apply(this, arguments).then(function(data) {
if(data.status === 401) {
console.log('not authorized, trying to get refresh cookie..')
const fetchIt = async () => {
let response = await fetch(`/api/token`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
});
}
fetchIt();
}
return data
});
};
})();
EDIT: To make it more clear what I am after. I need an interceptor like I described above to work so I don't have to do something like this after every fetch call:
getData() {
const getDataAsync = async () => {
let response = await fetch(`/api/loadData`, { method: 'POST' });
if(response.status === 401) {
let responseT = await fetch(`/api/token`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
});
if(responseT.status === 401) {
return responseT.status
}
if(responseT.status === 200) {
response = await fetch(`/api/loadData`, { method: 'POST' });
}
}
let data = await response.json();
//Do things with data
};
getDataAsync();
};
So basically the interceptor should:
Check if there is a 401, if so then:
fetch api/token
If api/token returns 401, it should just return that.
If api/token returns 200, it should run original fetch again
You can simple use originalFetch for token and await for response if response is 401 then you simply return empty response to first fetch call else you updated token and then let it go to next condition which will rerun old request.
let TEMP_API = {
'401': {
url: 'https://run.mocky.io/v3/7a98985c-1e59-4bfb-87dd-117307b6196c',
args: {}
},
'200': {
url: 'https://jsonplaceholder.typicode.com/todos/2',
args: {}
},
'404': {
url: 'https://jsonplaceholder.typicode.com/todos/1',
args: {
method: "POST",
credentials: "include"
}
}
}
const originalFetch = fetch;
fetch = function() {
let self = this;
let args = arguments;
return originalFetch.apply(self, args).then(async function(data) {
if (data.status === 200) console.log("---------Status 200----------");
if (data.status === 401) {
// request for token with original fetch if status is 401
console.log('failed');
let response = await originalFetch(TEMP_API['200'].url, TEMP_API['200'].args);
// if status is 401 from token api return empty response to close recursion
console.log("==========401 UnAuthorize.=============");
console.log(response);
if (response.status === 401) {
return {};
}
// else set token
// recall old fetch
// here i used 200 because 401 or 404 old response will cause it to rerun
// return fetch(...args); <- change to this for real scenarios
// return fetch(args[0], args[1]); <- or to this for real sceaerios
return fetch(TEMP_API['200'].url, TEMP_API['200'].args);
}
// condition will be tested again after 401 condition and will be ran with old args
if (data.status === 404) {
console.log("==========404 Not Found.=============");
// here i used 200 because 401 or 404 old response will cause it to rerun
// return fetch(...args); <- change to this for real scenarios
// return fetch(args[0], args[1]); <- or to this for real scenarios
return fetch(TEMP_API['200'].url, TEMP_API['200'].args);
sceaerios
} else {
return data;
}
});
};
(async function() {
console.log("==========Example1=============");
let example1 = await fetch(TEMP_API['404'].url, TEMP_API['404'].args);
console.log(example1);
console.log("==========Example2=============");
let example2 = await fetch(TEMP_API['200'].url, TEMP_API['200'].args);
console.log(example2);
console.log("==========Example3=============");
let example3 = await fetch(TEMP_API['401'].url, TEMP_API['401'].args);
console.log(example3);
})();
Example1 request made to api for 404 status which will cause the 404 condition to run which will then call 200 api after which response will be returned
Example2 request made to 200 api which will return 200 status code which will cause 200 condition to pass and run and return response
Example3 request made to api for 401 status which will cause 401 condition to pass which will then call 200 api and print response after which it will fall out of condition where you can set token which will then be used in another fetch request
Try retuning the fetch promise instead of awaiting that.
(function () {
const originalFetch = fetch;
fetch = function () {
return originalFetch.apply(this, arguments).then(function (data) {
if (data.status === 200) console.log("---------Status 200----------");
if (data.status === 404) {
console.log("==========404 Not Found.=============");
return fetch(`https://jsonplaceholder.typicode.com/todos/2`);
} else {
return data;
}
});
};
})();
function test(id) {
//will trigger 404 status
return fetch(`https://jsonplaceholder.typicode.com/todos/` + id, {
method: "POST",
credentials: "include",
});
}
test(1).then((i) => console.log(i));
Interceptor library for the native fetch command. It patches the global fetch method and allows you the usage in Browser, Node and Webworker environments.
fetch-retry It wraps any Fetch API package (eg: isomorphic-fetch, cross-fetch, isomorphic-unfetch and etc.) and retries requests that fail due to network issues. It can also be configured to retry requests on specific HTTP status codes.

ajax net::ERR_EMPTY_RESPONSE after waiting for response for 2 mins - node.js server

I have this code where the client uploads a file to the server through an AJAX POST request and then the server uploads that file to a cloud (cloudinary), and responds to the AJAX request after the upload is completed.
The problem occurs when the file upload takes longer than 2 minutes ( i timed it, since the beginning of the request till the error occurs).
Everything is working fine if the upload takes less than 2 minutes, And uploads that take longer than 2 minutes are completed after the error with no problem. but the client receives the empty response at the 2 minute mark.
server-side code:
router.route('/posts').post(middleware.isLoggedIn, function (req, res) {
var form = new multiparty.Form()
form.parse(req, function (err, fields, files) {
if (err) return err
cloudinary.v2.uploader.upload(files.content[0].path, { resource_type:
'auto' }, function (err, result) {
if (err) return err
console.log(result)
res.json({ result: result })
})
})
Client-side code:
function newPost (type, title, content) {
if (type === 'image') {
$('#newImageForm').addClass('loading')
} else if (type === 'video') {
$('#newVideoForm').addClass('loading')
} else if (type === 'gif') {
$('#newGifForm').addClass('loading')
}
var form = new FormData()
form.append('content', content)
form.append('type', type)
form.append('title', title)
$.ajax({
type: 'POST',
url: '/posts',
data: form,
processData: false,
contentType: false,
timeout: 0,
success: function (response) {
if (type === 'image') {
$('#newImageForm').removeClass('loading')
$('#newImageForm').fadeOut()
$('#imageTitle').val('')
$('#image').val('')
} else if (type === 'video') {
$('#newVideoForm').removeClass('loading')
$('#videoTitle').val('')
$('#video').val('')
$('#newVideoForm').fadeOut()
} else if (type === 'gif') {
$('#newGifForm').removeClass('loading')
$('#gifTitle').val('')
$('#gif').val('')
$('#newGifForm').fadeOut()
}
successMessage(response._id)
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
errorMessage()
}
})
}
Sounds like you are running into the internal timeout of nodejs request. https://nodejs.org/api/http.html#http_request_settimeout_timeout_callback
Try setting it to something higher with req.setTimeout(10000); or disable it with req.setTimeout(0)
When you are sending your request which takes long time from Ajax you have to do 2 things...
first thing is that you need to do is add timeout in your ajax request like following:
$.ajax({
url: "YOUR URL",
type: 'POST',
data: { path: JSON.stringify(p) },
dataType: 'json',
timeout: 1000000,
})
But this alone would not work if your listening server is not listening long requests so in node you do following:
var server = app.listen(PORT, function() {
});
server.timeout = 600000;
This 600000 is 10 minutes you can give longer value if required...
I hope this solves your problem....

Handle Status 304 response with AngularJS $http

If i have one API server then the API is send ajax data with JSON format :
{"status":304,"message":"Cannot delete data where PK is empty or > 1"}
how to AngularJS $http post call the status and message to alert bootbox?
here my AngularJS $http post
$http({
method: "POST",
url: apiUrl('disable_assethw'),
data: {
id: id
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(function successCallback(response) {
if(response.status == 304) {
bootbox.alert("Something went error.." + response.data.message);
} else {
$scope.getAssetHW();
}
}, function errorCallback(response) {
bootbox.alert("Something went error.." + response.status);
});
thanks for advise.
When doing a POST request with JavaScript objects as data, use the AngularJS default content type (which is automatically set to application/json). The $http service also automatically encodes JavaScript objects as JSON strings.
Only response with status in the range 200-299 are processed by the success handler. Status outside the range are processed by the rejection handler:
$http({
method: "POST",
url: apiUrl('disable_assethw'),
data: {
id: id
},
headers: {
̶'̶C̶o̶n̶t̶e̶n̶t̶-̶T̶y̶p̶e̶'̶:̶ ̶'̶a̶p̶p̶l̶i̶c̶a̶t̶i̶o̶n̶/̶x̶-̶w̶w̶w̶-̶f̶o̶r̶m̶-̶u̶r̶l̶e̶n̶c̶o̶d̶e̶d̶'̶
}
}).then(function successCallback(response) {
̶i̶f̶(̶r̶e̶s̶p̶o̶n̶s̶e̶.̶s̶t̶a̶t̶u̶s̶ ̶=̶=̶ ̶3̶0̶4̶)̶ ̶{̶
̶b̶o̶o̶t̶b̶o̶x̶.̶a̶l̶e̶r̶t̶(̶"̶S̶o̶m̶e̶t̶h̶i̶n̶g̶ ̶w̶e̶n̶t̶ ̶e̶r̶r̶o̶r̶.̶.̶"̶ ̶+̶ ̶r̶e̶s̶p̶o̶n̶s̶e̶.̶d̶a̶t̶a̶.̶m̶e̶s̶s̶a̶g̶e̶)̶;̶
̶}̶ ̶e̶l̶s̶e̶ ̶{̶
$scope.getAssetHW();
̶}̶
}, function errorCallback(response) {
//HANDLE 304 status HERE
if(response.status == 304) {
bootbox.alert("Something went error.." + response.data.message);
} else {
bootbox.alert("Something went error.." + response.status);
};
});
From the Docs:
A response status code between 200 and 299 is considered a success status and will result in the success callback being called. Any response status code outside of that range is considered an error status and will result in the error callback being called. Also, status codes less than -1 are normalized to zero. -1 usually means the request was aborted.
— AngularJS $http Service API Reference
Note: A status of -1 usually indicates the browser rejected the request with a CORS problem that violates same-origin policy.
you said it is json response and you used: application/x-www-form-urlencoded , which is wrong.
The best practice to handle rest/api call is:
Create 1 common/general function which is accessible in whole application which will manage your post api call(add api response to callback):
postAPICall(url, body, data) {
let headers = new Headers({'Content-Type': 'application/json'});
this.http
.post(url,
body, {
headers: headers
})
.map(
response => response.json())
.subscribe(
response => {
data(response);
},
err => data(this.handleError(err)); //handle error here
);
}
call this function wherever required(in component or service):
var yourJSONBody = {
"param-1": "",
"param-2": "",
//....
}
}
this.myCommonService.postAPICall("localhost:8080/app/", yourJSONBody, data => {
if (data.status == "304") {
//do stuff
//this.msgs.push({severity: 'error', detail: data.message});
}
else {
//do stuff
}
});
error handler function:
private handleError(error: any) {
let description = 'There was an error: ' + error.status;
let errors = {
errorcode: error.status,
errorstatus: error.statusText,
errordescription: description
};
return errors;
}

Error handling in AngularJS http get then construct

How can I handle an HTTP error, e.g. 500, when using the AngularJS "http get then" construct (promises)?
$http.get(url).then(
function(response) {
console.log('get',response)
}
)
Problem is, for any non 200 HTTP response, the inner function is not called.
You need to add an additional parameter:
$http.get(url).then(
function(response) {
console.log('get',response)
},
function(data) {
// Handle error here
})
You can make this bit more cleaner by using:
$http.get(url)
.then(function (response) {
console.log('get',response)
})
.catch(function (data) {
// Handle error here
});
Similar to #this.lau_ answer, different approach.
https://docs.angularjs.org/api/ng/service/$http
$http.get(url).success(successCallback).error(errorCallback);
Replace successCallback and errorCallback with your functions.
Edit: Laurent's answer is more correct considering he is using then. Yet I'm leaving this here as an alternative for the folks who will visit this question.
If you want to handle server errors globally, you may want to register an interceptor service for $httpProvider:
$httpProvider.interceptors.push(function ($q) {
return {
'responseError': function (rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
}
};
});
Docs: http://docs.angularjs.org/api/ng.$http
Try this
function sendRequest(method, url, payload, done){
var datatype = (method === "JSONP")? "jsonp" : "json";
$http({
method: method,
url: url,
dataType: datatype,
data: payload || {},
cache: true,
timeout: 1000 * 60 * 10
}).then(
function(res){
done(null, res.data); // server response
},
function(res){
responseHandler(res, done);
}
);
}
function responseHandler(res, done){
switch(res.status){
default: done(res.status + ": " + res.statusText);
}
}
I could not really work with the above. So this might help someone.
$http.get(url)
.then(
function(response) {
console.log('get',response)
}
).catch(
function(response) {
console.log('return code: ' + response.status);
}
)
See also the $http response parameter.

What is wrong with the following code for Parse.com Parse.Cloud define and Run and callbacks

Re-asked question with real code VS sudo code at
Having problems with some code on Parse.com's Cloud Code
/*
What am I doing wrong here?
I didn't want to re-write all my code to work with Parse.Cloud.run so I decided to make a function that would do that for me.
Error Message:
Update failed with Could not load triggers. The error was TypeError: Object # has no method 'request'
*/
Parse.Cloud.define("httpx", function(request, response) {
Parse.Cloud.httpRequest({
url: request.params.url,
method: request.params.method,
headers: request.params.headers
}, {
success: function(httpResponse) {
response.success(httpResponse);
},
error: function(httpResponse) {
response.error(httpResponse);
}
});
});
function sendRequest(path, method, callback) {
if (!initialized) {
throw 'not initialized, call initialize(username, password) first before calling the API';
}
// Allows for only 2 paramiters to be passed if no method passed.
if (typeof method == 'function') {
callback = method;
method = 'GET';
}
var type = (useHttps) ? 'http://' : 'https://';
////////////////////////////////////////
var request = Parse.Cloud.run("httpx", {
url: type + 'rest.website.com/' + path,
method: method,
headers: headers
}, {
success: function(httpResponse) {
if (callback) {
callback(httpResponse.text);
}
response.success(httpResponse.text);
},
error: function(httpResponse) {
callback(httpResponse.status);
response.error('Request failed with response code ' + httpResponse.status);
}
});
//////////////////////////////////////////
}
response.success(httpResponse.text);
where do you define response object?

Categories

Resources