为什么我的 Meteor API 调用抛出异常?
Why is my Meteor API call throwing an exception?
我有以下代码(基于“Meteor In Action”的第 242 和 243 页)尝试调用外部 API:
流星方法:
Meteor.methods({
. . .
'getTextAddrAsEmailAddr': function(phone) {
this.unblock;
var apiUrl = 'http://www.xminder.com/number.check.php?number=' + phone;
var response = Meteor.wrapAsync(apiCall) (apiUrl);
return response;
}
});
"dedicated function":
var apiCall = function (apiUrl, callback) {
try {
var response = HTTP.get(apiUrl).data;
callback(null, response);
} catch (error) {
if (error.response) {
var errorCode = error.response.data.code;
var errorMessage = error.response.data.message;
} else {
var errorCode = 500;
var errorMessage = 'Cannot access the API';
}
var myError = new Meteor.Error(errorCode, errorMessage);
callback(myError, null);
}
}
我是怎么称呼它的(一个 Meteor 方法调用另一个):
Meteor.methods({
'insertPerson': function(firstname, lastname, streetaddr1, streetaddr2, placename, stateorprov, zipcode, emailaddr, phone, notes) {
console.log('insertPerson reached'); // TODO: Remove before deploying
check(firstname, String);
. . .
console.log('phone is ' + phone);
var textAddrAsEmailAddr = Meteor.call("getTextAddrAsEmailAddr", phone, function(error, result) {
console.log("textAddrAsEmailAddr is " + textAddrAsEmailAddr);
console.log(result);
})
console.log('textAddrAsEmailAddr is ' + textAddrAsEmailAddr);
. . .
(Chrome) 浏览器控制台输出:
reached addPerson.submit form
methods.js:29 insertPerson reached
methods.js:41 phone is 2624908739
debug.js:41 Exception in callback of async function Error: Cannot access the API [500]
at apiCall (http://localhost:3000/app/both/methods.js?55690625ac0aa28550112db1c63d2b8912cc3410:30:19)
at http://localhost:3000/packages/meteor.js?9730f4ff059088b3f7f14c0672d155218a1802d4:195:23
at Meteor.methods.getTextAddrAsEmailAddr (http://localhost:3000/app/both/methods.js?55690625ac0aa28550112db1c63d2b8912cc3410:82:45)
at http://localhost:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:3911:25
at _.extend.withValue (http://localhost:3000/packages/meteor.js?9730f4ff059088b3f7f14c0672d155218a1802d4:971:17)
at _.extend.apply (http://localhost:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:3902:54)
at _.extend.call (http://localhost:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:3780:17)
at Meteor.methods.insertPerson (http://localhost:3000/app/both/methods.js?55690625ac0aa28550112db1c63d2b8912cc3410:52:38)
at http://localhost:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:3911:25
at _.extend.withValue (http://localhost:3000/packages/meteor.js?9730f4ff059088b3f7f14c0672d155218a1802d4:971:17)
methods.js:50 textAddrAsEmailAddr is undefined
methods.js:51 undefined
methods.js:53 textAddrAsEmailAddr is undefined
命令提示符输出:
I20151024-07:23:43.999(-7)? insertPerson reached
I20151024-07:23:44.021(-7)? phone is 2624908739
I20151024-07:23:44.641(-7)? textAddrAsEmailAddr is undefined
I20151024-07:23:44.649(-7)? null
I20151024-07:23:44.649(-7)? textAddrAsEmailAddr is undefined
所以结果是,命中了我的专用函数的 catch 块。为什么?我可以直接在浏览器中输入ApiUrl(http://www.xminder.com/number.check.php?number=2624908739)得到如下结果:
{"success":true,"data":{"number":"2624908739","status":"YES","carrier_name":"ATT Mobility","carrier_id":"","sms_address":"2624908739@txt.att.net","mms_address":"2624908739@mms.att.net"}}
我想要 sms_address 值,特别是;为什么我尝试访问此响应失败?
更新
我把代码改成这样:
var apiCall = function (apiUrl, callback) {
try {
var response = HTTP.get(apiUrl).data;
callback(null, response);
}
// catch (error) {
// if (error.response) {
// var errorCode = error.response.data.code;
// var errorMessage = error.response.data.message;
// }
// else {
// // var errorCode = 500;
// var errorCode = error.response.code;
// // var errorMessage = 'Cannot access the API';
// var errorMessage = error.response.message;
// }
// var myError = new Meteor.Error(errorCode, errorMessage);
// callback(myError, null);
// }
catch (error) { console.log(error) };
}
...并在命令提示符控制台中得到同样的结果:
I20151024-08:14:57.413(-7)? insertPerson reached
I20151024-08:14:57.434(-7)? phone is 2624908741
I20151024-08:14:58.248(-7)? textAddrAsEmailAddr is undefined
I20151024-08:14:58.255(-7)? null
I20151024-08:14:58.264(-7)? textAddrAsEmailAddr is undefined
...在浏览器控制台中:
reached addPerson.submit form
methods.js:25 insertPerson reached
methods.js:37 phone is 2624908741
methods.js:20 Error: Can't make a blocking HTTP call from the client; callback required.(…)
methods.js:46 textAddrAsEmailAddr is undefined
methods.js:47 undefined
methods.js:49 textAddrAsEmailAddr is undefined
注意: 将 catch 块更改为:
catch (error) {
console.log(error)
callback(error, null);
};
更新 2
尝试使用这个特定的 API 可能是失败的原因,无论如何,因为在浏览器中测试一个伪造的 phone 数字:
{"success":false,"error":"Too many requests during last 24h"}
更新 3
我将 Meteor 方法从 \both\ 文件夹移动到 \server\ 文件夹,但显然收效甚微。我在浏览器控制台中只得到这个:
reached addPerson.submit form
...以及命令提示符控制台中的这个 (still/again):
I20151024-13:02:23.539(-7)? insertPerson reached
I20151024-13:02:23.563(-7)? phone is 2624908743
I20151024-13:02:24.399(-7)? textAddrAsEmailAddr is undefined
I20151024-13:02:24.409(-7)? null
I20151024-13:02:24.410(-7)? textAddrAsEmailAddr is undefined
...但是 Jeroen Peeter 的代码与我得到的不同,我不知道如何将它合并到我现有的代码中;他说:
HTTP.get(apiUrl, function (error, data){
console.log( 'http.get ::', error, data);
});
...我有:
var apiCall = function (apiUrl, callback) {
try {
var response = HTTP.get(apiUrl).data;
callback(null, response);
}
catch (error) {
if (error.response) {
var errorCode = error.response.data.code;
var errorMessage = error.response.data.message;
}
else {
var errorCode = error.response.code;
var errorMessage = error.response.message;
}
var myError = new Meteor.Error(errorCode, errorMessage);
callback(myError, null);
}
...那么他的代码在哪里适合我的(非工作)代码?
更新 4
更改后:
var response = HTTP.get(apiUrl).data;
...为此:
var response = JSON.parse(HTTP.get(apiUrl).content;
...我在命令提示符控制台中得到:
=> Errors prevented startup:
While processing files with ecmascript (for target os.windows.x86_32):
server/methods.js:4:54: server/methods.js: Unexpected token (4:54)
=> Your application has errors. Waiting for file change.
第 4 行,字符 54 是 "content" 中的最后一个 "t"...???
HTTP.get 似乎是来自客户端而不是服务器的 运行。在那种情况下,阻塞调用是不可能的,因此会出现错误消息。您需要定义一个回调:
HTTP.get(apiUrl, function (error, data){
console.log( 'http.get ::', error, data);
});
如果您不想 运行 在客户端执行此操作(而且我认为您不希望那样),则需要确保在服务器上定义了您的方法。
将定义 Meteor.methods 的文件放在 server
文件夹中,或者用 if (Meteor.isServer) { ... }
包裹所有 Meteor.methods 以确保这些方法仅在服务器上定义。
if (Meteor.isServer) {
Meteor.methods({
. . .
'getTextAddrAsEmailAddr': function(phone) {
this.unblock;
var apiUrl = 'http://www.xminder.com/number.check.php?number=' + phone;
var response = Meteor.wrapAsync(apiCall) (apiUrl);
return response;
}
});
您使用 Meteor.wrapAsync 进行外部 API 调用的方式有问题。
这是我的做法(在 ecmascript2015 中)
let wrappedGetTextAddrAsEmailAddr = Meteor.wrapAsync(function (phone, callback) {
const apiUrl = `http://www.xminder.com/number.check.php?number=${phone}`;
HTTP.get(apiUrl, callback);
});
Meteor.methods(
{
// Call like: Meteor.call('insertPerson', {firstname: 'Bob', ...});
insertPerson(personData)
{
let textAddrAsEmailAddr;
console.log('insertPerson reached'); // TODO: Remove before deploying
check(personData, Object);
check(personData.firstname, String);
. . .
console.log(`phone is ${phone}`);
textAddrAsEmailAddr = wrappedGetTextAddrAsEmailAddr(personData.phone);
console.log(`textAddrAsEmailAddr is ${textAddrAsEmailAddr}`);
. . .
}
});
确保将上面的所有代码都放在服务器文件夹中,或者用 if (Meteor.isServer) { ... }
将它们包装起来
我有以下代码(基于“Meteor In Action”的第 242 和 243 页)尝试调用外部 API:
流星方法:
Meteor.methods({
. . .
'getTextAddrAsEmailAddr': function(phone) {
this.unblock;
var apiUrl = 'http://www.xminder.com/number.check.php?number=' + phone;
var response = Meteor.wrapAsync(apiCall) (apiUrl);
return response;
}
});
"dedicated function":
var apiCall = function (apiUrl, callback) {
try {
var response = HTTP.get(apiUrl).data;
callback(null, response);
} catch (error) {
if (error.response) {
var errorCode = error.response.data.code;
var errorMessage = error.response.data.message;
} else {
var errorCode = 500;
var errorMessage = 'Cannot access the API';
}
var myError = new Meteor.Error(errorCode, errorMessage);
callback(myError, null);
}
}
我是怎么称呼它的(一个 Meteor 方法调用另一个):
Meteor.methods({
'insertPerson': function(firstname, lastname, streetaddr1, streetaddr2, placename, stateorprov, zipcode, emailaddr, phone, notes) {
console.log('insertPerson reached'); // TODO: Remove before deploying
check(firstname, String);
. . .
console.log('phone is ' + phone);
var textAddrAsEmailAddr = Meteor.call("getTextAddrAsEmailAddr", phone, function(error, result) {
console.log("textAddrAsEmailAddr is " + textAddrAsEmailAddr);
console.log(result);
})
console.log('textAddrAsEmailAddr is ' + textAddrAsEmailAddr);
. . .
(Chrome) 浏览器控制台输出:
reached addPerson.submit form
methods.js:29 insertPerson reached
methods.js:41 phone is 2624908739
debug.js:41 Exception in callback of async function Error: Cannot access the API [500]
at apiCall (http://localhost:3000/app/both/methods.js?55690625ac0aa28550112db1c63d2b8912cc3410:30:19)
at http://localhost:3000/packages/meteor.js?9730f4ff059088b3f7f14c0672d155218a1802d4:195:23
at Meteor.methods.getTextAddrAsEmailAddr (http://localhost:3000/app/both/methods.js?55690625ac0aa28550112db1c63d2b8912cc3410:82:45)
at http://localhost:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:3911:25
at _.extend.withValue (http://localhost:3000/packages/meteor.js?9730f4ff059088b3f7f14c0672d155218a1802d4:971:17)
at _.extend.apply (http://localhost:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:3902:54)
at _.extend.call (http://localhost:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:3780:17)
at Meteor.methods.insertPerson (http://localhost:3000/app/both/methods.js?55690625ac0aa28550112db1c63d2b8912cc3410:52:38)
at http://localhost:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:3911:25
at _.extend.withValue (http://localhost:3000/packages/meteor.js?9730f4ff059088b3f7f14c0672d155218a1802d4:971:17)
methods.js:50 textAddrAsEmailAddr is undefined
methods.js:51 undefined
methods.js:53 textAddrAsEmailAddr is undefined
命令提示符输出:
I20151024-07:23:43.999(-7)? insertPerson reached
I20151024-07:23:44.021(-7)? phone is 2624908739
I20151024-07:23:44.641(-7)? textAddrAsEmailAddr is undefined
I20151024-07:23:44.649(-7)? null
I20151024-07:23:44.649(-7)? textAddrAsEmailAddr is undefined
所以结果是,命中了我的专用函数的 catch 块。为什么?我可以直接在浏览器中输入ApiUrl(http://www.xminder.com/number.check.php?number=2624908739)得到如下结果:
{"success":true,"data":{"number":"2624908739","status":"YES","carrier_name":"ATT Mobility","carrier_id":"","sms_address":"2624908739@txt.att.net","mms_address":"2624908739@mms.att.net"}}
我想要 sms_address 值,特别是;为什么我尝试访问此响应失败?
更新
我把代码改成这样:
var apiCall = function (apiUrl, callback) {
try {
var response = HTTP.get(apiUrl).data;
callback(null, response);
}
// catch (error) {
// if (error.response) {
// var errorCode = error.response.data.code;
// var errorMessage = error.response.data.message;
// }
// else {
// // var errorCode = 500;
// var errorCode = error.response.code;
// // var errorMessage = 'Cannot access the API';
// var errorMessage = error.response.message;
// }
// var myError = new Meteor.Error(errorCode, errorMessage);
// callback(myError, null);
// }
catch (error) { console.log(error) };
}
...并在命令提示符控制台中得到同样的结果:
I20151024-08:14:57.413(-7)? insertPerson reached
I20151024-08:14:57.434(-7)? phone is 2624908741
I20151024-08:14:58.248(-7)? textAddrAsEmailAddr is undefined
I20151024-08:14:58.255(-7)? null
I20151024-08:14:58.264(-7)? textAddrAsEmailAddr is undefined
...在浏览器控制台中:
reached addPerson.submit form
methods.js:25 insertPerson reached
methods.js:37 phone is 2624908741
methods.js:20 Error: Can't make a blocking HTTP call from the client; callback required.(…)
methods.js:46 textAddrAsEmailAddr is undefined
methods.js:47 undefined
methods.js:49 textAddrAsEmailAddr is undefined
注意: 将 catch 块更改为:
catch (error) {
console.log(error)
callback(error, null);
};
更新 2
尝试使用这个特定的 API 可能是失败的原因,无论如何,因为在浏览器中测试一个伪造的 phone 数字:
{"success":false,"error":"Too many requests during last 24h"}
更新 3
我将 Meteor 方法从 \both\ 文件夹移动到 \server\ 文件夹,但显然收效甚微。我在浏览器控制台中只得到这个:
reached addPerson.submit form
...以及命令提示符控制台中的这个 (still/again):
I20151024-13:02:23.539(-7)? insertPerson reached
I20151024-13:02:23.563(-7)? phone is 2624908743
I20151024-13:02:24.399(-7)? textAddrAsEmailAddr is undefined
I20151024-13:02:24.409(-7)? null
I20151024-13:02:24.410(-7)? textAddrAsEmailAddr is undefined
...但是 Jeroen Peeter 的代码与我得到的不同,我不知道如何将它合并到我现有的代码中;他说:
HTTP.get(apiUrl, function (error, data){
console.log( 'http.get ::', error, data);
});
...我有:
var apiCall = function (apiUrl, callback) {
try {
var response = HTTP.get(apiUrl).data;
callback(null, response);
}
catch (error) {
if (error.response) {
var errorCode = error.response.data.code;
var errorMessage = error.response.data.message;
}
else {
var errorCode = error.response.code;
var errorMessage = error.response.message;
}
var myError = new Meteor.Error(errorCode, errorMessage);
callback(myError, null);
}
...那么他的代码在哪里适合我的(非工作)代码?
更新 4
更改后:
var response = HTTP.get(apiUrl).data;
...为此:
var response = JSON.parse(HTTP.get(apiUrl).content;
...我在命令提示符控制台中得到:
=> Errors prevented startup:
While processing files with ecmascript (for target os.windows.x86_32):
server/methods.js:4:54: server/methods.js: Unexpected token (4:54)
=> Your application has errors. Waiting for file change.
第 4 行,字符 54 是 "content" 中的最后一个 "t"...???
HTTP.get 似乎是来自客户端而不是服务器的 运行。在那种情况下,阻塞调用是不可能的,因此会出现错误消息。您需要定义一个回调:
HTTP.get(apiUrl, function (error, data){
console.log( 'http.get ::', error, data);
});
如果您不想 运行 在客户端执行此操作(而且我认为您不希望那样),则需要确保在服务器上定义了您的方法。
将定义 Meteor.methods 的文件放在 server
文件夹中,或者用 if (Meteor.isServer) { ... }
包裹所有 Meteor.methods 以确保这些方法仅在服务器上定义。
if (Meteor.isServer) {
Meteor.methods({
. . .
'getTextAddrAsEmailAddr': function(phone) {
this.unblock;
var apiUrl = 'http://www.xminder.com/number.check.php?number=' + phone;
var response = Meteor.wrapAsync(apiCall) (apiUrl);
return response;
}
});
您使用 Meteor.wrapAsync 进行外部 API 调用的方式有问题。
这是我的做法(在 ecmascript2015 中)
let wrappedGetTextAddrAsEmailAddr = Meteor.wrapAsync(function (phone, callback) {
const apiUrl = `http://www.xminder.com/number.check.php?number=${phone}`;
HTTP.get(apiUrl, callback);
});
Meteor.methods(
{
// Call like: Meteor.call('insertPerson', {firstname: 'Bob', ...});
insertPerson(personData)
{
let textAddrAsEmailAddr;
console.log('insertPerson reached'); // TODO: Remove before deploying
check(personData, Object);
check(personData.firstname, String);
. . .
console.log(`phone is ${phone}`);
textAddrAsEmailAddr = wrappedGetTextAddrAsEmailAddr(personData.phone);
console.log(`textAddrAsEmailAddr is ${textAddrAsEmailAddr}`);
. . .
}
});
确保将上面的所有代码都放在服务器文件夹中,或者用 if (Meteor.isServer) { ... }