我正在尝试按照 Adam Freeman 的 Apress“Pro JavaScript for Web Apps”中的建议设置 Node。我已经在我的 Mac (OS 10.6.8) 机器上安装了 Node (0.10.7) 并运行了各种示例来证明 Node 正在工作,例如
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
所以我可以从终端启动 Node,然后在浏览器中获得“Hello World”。到目前为止一切似乎都很好。
然后我发出命令来添加本书建议的某些包:
npm install node-static jqtpl
这也有效;它通过 HTTP GET 命令获取了一堆东西,并且似乎安装它们没有错误。
Adam 的示例文件有两个组成部分:有一个“server.js”文件,还有一个包含每章示例的“content”目录。我的问题似乎与文件和内容目录之间的路径有关,或者基于谷歌搜索此 EACCESS 错误,某些权限问题。我尝试了各种配置(将 server.js 放在我的“dave”目录中,这是我在 Mac 上的用户目录,并将“content”目录作为“dave”的子目录;这似乎是他想要的根据书中的说明;或者,将 server.js 放在内容目录中;由于路径错误,这似乎不起作用。
无论如何,当我将“server.js”放在“dave”目录中,并让“content”也存在于“dave”目录中时(实际上,对于 server.js 所在目录中的任何目录来说,这似乎都是正确的,和内容也驻留在同一个目录中),我得到这个:
dave$ node server.js
Ready on port 80
events.js:72
throw er; // Unhandled 'error' event
^
Error: listen EACCES
at errnoException (net.js:884:11)
at Server._listen2 (net.js:1003:19)
at listen (net.js:1044:10)
at Server.listen (net.js:1110:5)
at Object.<anonymous> (/Users/dave/NodeJsTesting/server.js:92:34)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
警告:我几乎是 Unix 新手(我是 .Net 开发人员),所以可能只是我不了解 Unix 权限、路径或 PATH 变量。Adam 谈到将东西放在“你的 Node.js 目录中”,我认为这可能不够具体。我对本地机器上的行为的观察是 NODE 可执行文件显然在我的 PATH 中,因为我可以从我的用户目录执行上述命令。无论如何,我怀疑这是一些权限问题。
为了完整起见,这里是亚当的代码:
var http = require('http');
var url = require('url');
var fs = require('fs');
var nodestatic = require('node-static');
var jqtpl = require("jqtpl");
var querystring = require('querystring');
var fileserver = new(nodestatic.Server)("./content", { cache: 1 });
var productData = JSON.parse(fs.readFileSync("./content/products.json"));
function handleRequest(req, res) {
console.log(req.method + " request for " + req.url);
if (req.method == "POST") {
var fullBody = '';
req.on('data', function(chunk) {fullBody += chunk.toString();});
req.on('end', function() {
var data = createDataObject(querystring.parse(fullBody));
if (req.headers['x-http-method-override']) {
var productID = req.url.split("/").pop();
switch (req.headers['x-http-method-override']) {
case "delete":
data.deleteItem(productID);
break;
case "put":
var item = data.getItem(productID);
if (item) {
item.name = data.getAndRemoveDataProp("name");
item.price = data.getAndRemoveDataProp("price")
}
break;
}
writeJSONData(res, productData);
} else {
switch (req.url) {
case "/formecho":
case "/basket":
case "/basket":
case "/shipping":
case "/summary":
res.write(jqtpl.tmpl(loadTemplate(req.url.substring(1)), data));
break;
}
res.end();
}
});
} else {
if (req.url.indexOf("/shortJSONP") == 0) {
var callback = querystring.parse(url.parse(req.url).query)["callback"];
res.setHeader("Content-Type", "text/javascript");
res.write(callback + "(" + JSON.stringify([productData[0]]) + ")");
res.end();
} else {
if (req.headers["origin"] && req.headers["origin"].indexOf("cheeselux") > -1) {
res.setHeader("Access-Control-Allow-Origin", req.headers["origin"]);
}
switch (req.url) {
case "/cheeselux.appcache":
fileserver.serveFile("cheeselux.appcache", 200,
{"Content-Type": "text/cache-manifest"}, req, res);
break;
case "/products.json.slow":
setTimeout(function() {
fileserver.serveFile("products.json", 200, null, req, res);
}, 1000);
break;
case "/shortJSONList":
writeJSONData(res, [productData[0]]);
break;
case "/admin/products":
writeJSONData(res, productData);
break;
default:
if (req.url == "/") {
req.url = "/example.html";
}
fileserver.serve(req, res);
break;
};
}
}
}
http.createServer(handleRequest).listen(80);
console.log("Ready on port 80");
function loadTemplate(name) {
return fs.readFileSync("content/" + name + ".html").toString();
}
function writeJSONData(res, data) {
res.setHeader("Content-Type", "application/json");
res.write(JSON.stringify(data));
res.end();
}
function createDataObject(reqData) {
var data = {
properties: [],
getItem: function(id) {
for (var i = 0; i < productData.length; i++) {
for (var j = 0; j < productData[i].items.length; j++) {
if (productData[i].items[j].id == id) {
return productData[i].items[j];
}
}
}
return null;
},
deleteItem: function(id) {
for (var i = 0; i < productData.length; i++) {
for (var j = 0; j < productData[i].items.length; j++) {
if (productData[i].items[j].id == id) {
productData[i].items.splice(j, 1);
}
}
}
},
getProp: function (id, prop) {
for (var i = 0; i < productData.length; i++) {
for (var j = 0; j < productData[i].items.length; j++) {
if (productData[i].items[j].id == id) {
return productData[i].items[j][prop];
}
}
}
return "";
},
getAndRemoveDataProp: function(prop) {
for (var i = 0; i < this.properties.length; i++) {
if (this.properties[i].propName == prop) {
var result = this.properties[i].propVal;
this.properties.splice(i, 1);
return result;
}
}
return "";
},
total: 0,
getSubtotal: function(id, quantity) {
var price = this.getProp(id, "price") * quantity;
this.total += price;
return price;
}
}
for (var prop in reqData) {
data.properties.push({propName: prop, propVal: reqData[prop]})
}
return data;
}