I try to use request npm package in Meteor 1.3.2.4 as synchronous.
Based on this Meteor guide article, first I try to use Meteor.bindEnvironment
like below:
import request from 'request';
let result = {success: false, error: null, content: ""};
let requestOptions = {
url: <MY-URL-HERE>
};
request(requestOptions, Meteor.bindEnvironment((error, response, html) => {
if (!error && response.statusCode == 200) {
result.success = true;
result.content = html;
result.error = null;
}
else {
result.success = false;
result.content = "";
result.error = error;
}
}));
But it seems still async call.
In next step I try to use Meteor.wrapAsync
based on this answer on meteor forum, this is next try code:
import request from 'request';
let result = {success: false, error: null, content: ""};
let requestOptions = {
url: <MY-URL-HERE>
};
let func = function (options, callback) {
request(options, function (error, response, body) {
console.log("error: " + JSON.stringify(error));
console.log("response: " + JSON.stringify(response));
console.log("body: " + JSON.stringify(body));
callback(error, {response, body});
});
};
let {error, response, body} = syncRequest(requestOptions);
console.log("error2: " + JSON.stringify(error));
console.log("response2: " + JSON.stringify(response));
console.log("body2: " + JSON.stringify(body));
if (response.statusCode == 200) {
result.success = true;
result.content = body;
result.error = null;
}
else {
result.success = false;
result.content = "";
result.error = error;
}
This code works fine except request contain error. When request contain error (for example url is incorrect) this break with below exception:
I20160518-08:24:22.180(4.5)? error: {}
I20160518-08:24:22.181(4.5)? response: undefined
I20160518-08:24:22.182(4.5)? body: undefined
W20160518-08:24:22.839(4.5)? (STDERR) TypeError: The header content contains invalid characters
W20160518-08:24:22.839(4.5)? (STDERR) at Object.Future.wait (/home/cyc/.meteor/packages/meteor-tool/.1.3.2_4.7bk6xv++os.linux.x86_64+web.browser+web.cordova/mt-os.linux.x86_64/dev_bundle/server-lib/node_modules/fibers/future.js:420:15)
W20160518-08:24:22.839(4.5)? (STDERR) at packages/meteor/helpers.js:119:1
W20160518-08:24:22.839(4.5)? (STDERR) at Object.getContent (server/lib/get_content.js:51:26)
W20160518-08:24:22.839(4.5)? (STDERR) at Object.fetch (server/lib/get_content.js:205:27)
W20160518-08:24:22.839(4.5)? (STDERR) at Object.fetchSource (server/lib/get_content.js:369:31)
W20160518-08:24:22.840(4.5)? (STDERR) at [object Object].action (server/controller/postsController.js:79:39)
W20160518-08:24:22.840(4.5)? (STDERR) at [object Object].handle (packages/iron_middleware-stack/lib/handler.js:74:1)
W20160518-08:24:22.840(4.5)? (STDERR) at boundNext (packages/iron_middleware-stack/lib/middleware_stack.js:251:1)
W20160518-08:24:22.840(4.5)? (STDERR) at runWithEnvironment (packages/meteor/dynamics_nodejs.js:110:1)
W20160518-08:24:22.840(4.5)? (STDERR) at packages/meteor/dynamics_nodejs.js:123:1
W20160518-08:24:22.840(4.5)? (STDERR) - - - - -
W20160518-08:24:22.840(4.5)? (STDERR) at ClientRequest.OutgoingMessage.setHeader (http.js:733:13)
W20160518-08:24:22.840(4.5)? (STDERR) at new ClientRequest (http.js:1429:14)
W20160518-08:24:22.840(4.5)? (STDERR) at Object.exports.request (http.js:1899:10)
W20160518-08:24:22.841(4.5)? (STDERR) at Request.start (/home/cyc/Programming/Projects/content/Sources/node_modules/request/request.js:753:32)
W20160518-08:24:22.841(4.5)? (STDERR) at Request.end (/home/cyc/Programming/Projects/content/Sources/node_modules/request/request.js:1418:10)
W20160518-08:24:22.841(4.5)? (STDERR) at end (/home/cyc/Programming/Projects/content/Sources/node_modules/request/request.js:580:14)
W20160518-08:24:22.841(4.5)? (STDERR) at Object._onImmediate (/home/cyc/Programming/Projects/content/Sources/node_modules/request/request.js:594:7)
W20160518-08:24:22.841(4.5)? (STDERR) at processImmediate [as _immediateCallback] (timers.js:363:15)
Now I have two question:
Meteor.bindEnvironment
simply makes sure that your callback is called in the context you were using when calling the original function. It does not supposed to make your code appear synchronous. If you did not use it, your callback will not be run inside a fiber (which will prevent you from performing some actions) and you won't have access to certain variables (such as the current user). There is an excellent video by Crhis Mather on the subject (keep in mind that some of the code is a bit outdated, but the core is still valid).
Meteor.wrapAsync
wraps functions that expect a callback with the standard error-first 2 arguments signature. Since the request
does not adhere to this exact standard (it uses a callback with 3 arguments), the forum post suggested wrapping it with a function that expects a callback with the aforementioned 2 arguments.
The convention is that the result of calling a "wrapped" function is the value of the second argument passed to the callback (the data
), and an error is thrown if there is one.
Therefore, as it is now, you can wrap the function with a try..catch
block and see if there is an error.
Another option is to never raise an error:
let func = function (options, callback) {
request(options, function (error, response, body) {
callback(null, {response, body, error});
});
};
Which will always result in a {response, body, error}
object, where error
will be null
if there is no error.
I think that the http package is perfectly valid and does pretty much the same for you with convenience methods (e.g, the general call
, and specific get
and post
):
For example:
import { HTTP } from 'meteor/http';
const result = HTTP.get('https://my-url.com');