When Promisifying a XMLHttpRequest, how to catch a throw Error - javascript

After I've Promisified my XMLHttpRequest, like so:
var Request = (function() {
var get = function(url){
return request('GET', url);
},
post = function(url){
return request('POST', url);
},
request = function(method, url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function(e){
if (xhr.status === 200) {
resolve(xhr);
} else {
reject(Error('XMLHttpRequest failed; error code:' + xhr.statusText));
}
},
xhr.onerror = reject;
xhr.send();
});
};
return {
get: get,
post: post,
request: request
}
})();
I'd like to catch all network related errors, which this snippet already does. Now, when I chain my .then calls when the XHR calls are finished, I can pass around the result of the Ajax call.
Here is my question:
When I throw an Error in any .then branch, it will not get caught by the catch clause.
How can I achieve this?
Note that the throw new Error("throw error"); will not be caught in the catch clause....
For the entire code, see http://elgervanboxtel.nl/site/blog/xmlhttprequest-extended-with-promises
Here is my example code:
Request.get( window.location.href ) // make a request to the current page
.then(function (e) {
return e.response.length;
})
.then(function (responseLength) {
// log response length
console.info(responseLength);
// throw an error
throw new Error("throw error");
})
.catch(function(e) { // e.target will have the original XHR object
console.log(e.type, "readystate:", e.target.readyState, e);
});

The problem is, that the error gets thrown before your then block gets called.
Solution
Request
.get('http://google.com')
.catch(function(error) {
console.error('XHR ERROR:', error);
})
.then(function(responseLength) {
// log response length
console.info(responseLength);
// throw an error
throw new Error("throw error");
})
.catch(function(error) {
// e.target will have the original XHR object
console.error('SOME OTHER ERROR', error);
});
Hint
Why are you not using fetch()?

Related

GET Request with JS

i need to send a GET request from my JavaScript function to my python flask app. However, i tried to type the URL with the parameters manually and it worked. But i can't send the same request in a JS function. Response type is HTML.
This is how the URL should look like:
http://127.0.0.1:5000/books?rank=2&topic=Self improvement
I tried this, but it didn't work:
function sendRequest() {
const xhr = new XMLHttpRequest();
xhr.open('GET', '/books', {
rank: rank,
topic: topic
});
xhr.onload = function() {
console.log(xhr.response);
}
xhr.send();
}
What the URL looked like with this try:
http://127.0.0.1:5000/books
Please help!
You're trying to pass the parameters in a POST body (the third argument to open). That won't work for a GET, they have to be in the URL.
The easiest and least error-prone way is to use URLSearchParams (thank you Christopher for pointing that out when I forgot!):
const url = "/books?" + new URLSearchParams({rank, title});
Live Example:
const rank = 42;
const title = "Life, the Universe, and Everything";
const url = "/books?" + new URLSearchParams({rank, title});
console.log(url);
These days, you'd usually use the more modern fetch rather than XMLHttpRequest:
function sendRequest() {
const url = "/books?" + new URLSearchParams({rank, title});
fetch(url)
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
return response.text(); // Or `.json()` or one of the others
})
.then((data) => {
console.log(data);
})
.catch((error) => {
// ...handle/report error...
});
}
But if you prefer to use XMLHttpRequest, put the parameters in the URL (and handle errors):
function sendRequest() {
const xhr = new XMLHttpRequest();
const url = "/books?" + new URLSearchParams({rank, title});
xhr.open("GET", url);
xhr.onload = function () {
console.log(xhr.response);
};
xhr.onerror = function () {
// ...handle/report error...
};
xhr.send();
}
(You can also use string concatenation and encodeURIComponent to build the URL, but it's more work and more error-prone. :-) )
I made a code with fetch() based on the comments above, let me know if you get any errors. Hope this helps, XMLHttpRequest() is not used much due to its complexity.
async function sendRequest(){
const url = 'your URL';
await fetch(url,{
method: "GET",
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
}
}).then(response=>{
if(response.status !=200) {
throw new Error(`HTTP ERROR:${response.status}`);
}
return response.text()
}
).then(data => {
console.log(data);
// convert to html
}).catch(err => {
console.log(err);
})
}

How can i apply callback to get the http response in a method

I am attempting to write a method so that i pass the url and application name and it return the response. I read that I can apply callback to resolve this but I am not able to resolve the issue. Any help would be appreciated.
Please find below my code snippet.
var response = getResponse(url,applicationName)
console.log("response from getResponse \n" +response);
function getResponse(url,applicationName){
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
"application": applicationName
}));
xhr.onload = function() {
console.log(this.responseText);
}
return xhr.responseText;
}
You can use the onreadystatechange method to handle XHR responses, try this:
//XHR POST
const xhr = new XMLHttpRequest; // creates new object
const url = 'https://api-to-call.com/endpoint';
const data = JSON.stringify({"application": applicationName}); // converts data to a string
xhr.responseType = 'json';
xhr.onreadystatechange = () => {
if(xhr.readyState === XMLHttpRequest.DONE) {
return xhr.response;
}
}
xhr.open('POST', url); // opens request
xhr.send(data); // sends object
you should use promise instead of callback and do something like that.
const url = "https://httpbin.org/post";
const applicationName = "test";
getResponse(url, applicationName)
.then(response => {
//work here, not outside
console.log(response);
})
.catch(error => {
console.log(error);
})
function getResponse(url, applicationName) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
"application": applicationName
}));
xhr.onload = function() {
// print JSON response
if (xhr.status >= 200 && xhr.status < 300) { // if valid
// work here
const response = JSON.parse(xhr.response.replace(/"/g, '"'));
const data = JSON.parse(response.data.replace(/"/g, '"'));
resolve(data);
}
reject(xhr.response); // reject and return the response if not valid
}
})
}
If you want to learn more about asynchronous https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Concepts, I invite you to go to this website to learn a little more about the promise.
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Promises
The callback is executed after the code returned xhr.responseText. So that means xhr.responseText returns null.
I would recommend using the fetch API opposed to the older XMLHttpRequest you are using now. The fetch API is basically a Promise based XMLHttpRequest.
Your function would look something like:
async function getResponse( url, applicationName ) {
const json = JSON.stringify({
"application": applicationName
});
return fetch( url, {method: 'POST', headers: { 'Content-Type':'application/json'}, body: json} );
}
// access like this
getResponse( url, applicationName)
.then( response => { console.log(response) });
async function someFunction( url, applicationName ) {
// or pause the code while the request is fetched by using await, note that you need to be in a function that is declared async to use this approach.
const response = await getResponse( url, applicationName );
}
Fetch documentation can be found at MDN: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

Request to IP camera Javascript

i'm trying to make a request into an IP camera, but the camera needs Basic authentication, i need to get this request and show in an Iframe or img, but i don't know how to get that, i see how the request to the camera stays always waiting to finish an send some data, but i don't know how to get it and pass to the Iframe, thank's for any help.
Code Example:
function makeRequest(method, url, data) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.setRequestHeader(
"Authorization",
"Basic " + btoa("user:password")
);
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(xhr.response);
} else {
reject({
status: this.status,
statusText: xhr.statusText,
});
}
};
xhr.onerror = function () {
reject({
status: this.status,
statusText: xhr.statusText,
});
};
if (method == "POST" && data) {
xhr.send(data);
} else {
xhr.send();
}
});
}
//GET example
makeRequest("GET", "http://ip_camera_url").then(
function (data) {
var results = JSON.parse(data);
console.log(results);//Never come here
}
);
UPDATE:
First the request return status 200
then keeps sending data
but the request never ends and i can't get the result or the images the ip camera returns.
this is the message in the tab timing into the network option.

Serverless nodejs skip https request

I want to send a https request and process the result with serverless framework, but it seems not working.
Nodejs (serverless) skip my request and jump directly to the last result without waiting https reponse
here my function:
import { APIGatewayEvent, Callback, Context, Handler } from "aws-lambda";
var AWS = require("aws-sdk");
const querystring = require('querystring');
const https = require('https');
const TOKEN: String = "token";
export const hello: Handler = (
event: APIGatewayEvent,
context: Context,
cb: Callback
) => {
function https_request() {
var postData = querystring.stringify({
query: "query"
});
var options = {
hostname: 'example.com',
port: 443,
path: '/path',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': TOKEN
}
};
return new Promise(function(resolve, reject) {
console.log("before request")
var req = https.request(options, (res) => {
console.log('statusCode:', res.statusCode);
console.log('headers:', res.headers);
if (res.statusCode !== 200) {
// REJECT IF THE RESPONSE WASN'T AS EXPECTED
return reject(new Error('Request failed'));
}
res.on('data', (d) => {
process.stdout.write(d);
resolve(d); // RESOLVE ON SUCCESS WITH EXPECTED DATA
});
});
req.on('error', (e) => {
console.error(e);
reject(e); // REJECT ON REQUEST ERROR
});
// req.write(postData);
req.end();
})
}
let x:any;
async function myAsyncF() {
x= await https_request();
console.log(x.body)
return x.body
}
myAsyncF()
const response = {
statusCode: 200,
body: JSON.stringify({
message: x,
input: event
})
};
cb(null, response);
};
I used the async await, but nothing is returned (i should receive at least an error if there is some king of network error)
here is my output:
before request
{
"statusCode": 200,
"body": "{\"input\":\"\"}"
}
is there something missing ?
Thank you in advance
At no point do you resolve your Promise, I've no idea what you deem a "successful" request but here's an example that should get you on the right track
return new Promise(function(resolve, reject) {
console.log("before request")
var req = https.request(options, (res) => {
console.log('statusCode:', res.statusCode);
console.log('headers:', res.headers);
if (res.statusCode !== 200) {
// REJECT IF THE RESPONSE WASN'T AS EXPECTED
return reject(new Error('Request failed'));
}
res.on('data', (d) => {
process.stdout.write(d);
resolve(d); // RESOLVE ON SUCCESS WITH EXPECTED DATA
});
});
req.on('error', (e) => {
console.error(e);
reject(e); // REJECT ON REQUEST ERROR
});
req.write(postData);
req.end();
})
Remove process.stdout.write will is not a great practice as you are running your code on lambda, use a context object or a callback function to return
process.stdout.write(d) is write operation on file, sometimes it silently fails as well, running on EC2, ECS your solution looks gem. But on serverless it's always better to avoid file operation functions

How to wrap JavaScript fetch in a function - unhandled promise rejection

I'm trying to write a wrapper function for the JavaScript fetch command.
I took the example code from this post:
function fetchAPI(url, data, method = 'POST') {
const headers = {
'Authorization': `Token ${getAuthToken()}`,
};
return fetch(url, { headers, 'method': method, 'body': data })
.then(response => {
if (response.ok) {
const contentType = response.headers.get('Content-Type') || '';
if (contentType.includes('application/json')) {
return response.json().catch(error => {
return Promise.reject(new Error('Invalid JSON: ' + error.message));
});
}
if (contentType.includes('text/html')) {
return response.text().then(html => {
return {
'page_type': 'generic',
'html': html
};
}).catch(error => {
return Promise.reject(new Error('HTML error: ' + error.message));
});
}
return Promise.reject(new Error('Invalid content type: ' + contentType));
}
if (response.status === 404) {
return Promise.reject(new Error('Page not found: ' + url));
}
return response.json().then(res => {
// if the response is ok but the server rejected the request, e.g. because of a wrong password, we want to display the reason
// the information is contained in the json()
// there may be more than one error
let errors = [];
Object.keys(res).forEach((key) => {
errors.push(`${key}: ${res[key]}`);
});
return Promise.reject(new Error(errors)
);
});
}).catch(error => {
return Promise.reject(new Error(error.message));
});
};
And I'm calling it like this:
fetchAPI('/api/v1/rest-auth/password/change/', formData).then(response => {
console.log('response ', response);
});
Edit: I have modified the code to display information returned by the server if the request is ok but refused, for example because of an invalid password. You have to interrogate the response json if ok == false.
A valid URL fetch is fine. But if there is an error, I see an Unhandled Rejection (Error): error message.
Why is it that the rejects are unhandled even though they are in catch blocks? What's the secret sauce here?
The way to avoid an unhandled promise rejection, is to handle it:
fetchAPI('/api/v1/rest-auth/password/change/', formData).then(response => {
console.log('response ', response);
}).catch(error => {
// do something meaningful here.
});;

Categories