1,服务端JavaScript
JavaScript是一门“完整”的语言: 它可以使用在不同的上下文中,其能力与其他同类语言相比有过之而无不及。 Node.js事实上就是另外一种上下文,它允许在后端(脱离浏览器环境)运行JavaScript代码。 Node.js事实上既是一个运行时环境,同时又是一个库。
2,Hello World
创建server.js
var http = require("http"); http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); }).listen(8888);在cmd中运行node server.js 打开浏览器访问http://localhost:8888/ 分析:
第一行请求(require)Node.js自带的 http 模块,并且把它赋值给 http 变量调用http模块提供的函数: createServer。这个函数会返回一个对象,这个对象有一个 listen 的方法,这个方法有一个数值参数,指定这个HTTP服务器监听的端口号。在JavaScript中,函数和其他变量一样都是可以被传递的。Node.js是事件驱动的我们给某个方法传递了一个函数,这个方法在有相应事件发生时调用这个函数来进行 回调3,服务启动和请求执行流程分析
var http = require("http"); function onRequest(request, response) { console.log("Request received."); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); } http.createServer(onRequest).listen(8888) console.log("Server has started.");运行node server.js时,会马上在命令行上输出“Server has started.”。当我们向服务器发出请求(在浏览器访问http://localhost:8888/ ),“Request received.”这条消息就会在命令行中出现 这就是事件驱动的异步服务器端JavaScript和它的回调
当我们在服务器访问网页时,我们的服务器可能会输出两次“Request received.”。那是因为大部分浏览器都会在你访问 http://localhost:8888/ 时尝试读取 http://localhost:8888/favicon.ico
4,创建自己的模块 server.js
var http = require("http"); function start() { function onRequest(request, response) { console.log("Request received."); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;创建 index.js
var server = require("./server"); server.start();运行node index.js时 把我们的应用的不同部分放入不同的文件里,并且通过生成模块的方式把它们连接到一起了
5,如何来进行请求的“路由” 为路由提供请求的URL和其他需要的GET及POST参数,随后路由需要根据这些数据来执行相应的代码 server.js
var http = require("http"); var url = require("url"); function start(route) { function onRequest(request, response) { var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); route(pathname); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;router.js
function route(pathname) { console.log("About to route a request for " + pathname); } exports.route = route;把路由和服务器整合起来 index.js
var server = require("./server"); var router = require("./router"); server.start(router.route);启动应用node index.js,随后请求一个URL,你将会看到应用输出相应的信息,这表明我们的HTTP服务器已经在使用路由模块了,并会将请求的路径传递给路由
6,函数式编程 将函数作为参数传递并不仅仅出于技术上的考量 Steve Yegge的大作名词王国中的死刑
7,路由给真正的请求处理程序 创建一个叫做requestHandlers的模块,并对于每一个请求处理程序,添加一个占位用函数,随后将这些函数作为模块的方法导出 requestHandlers.js
function start() { console.log("Request handler 'start' was called."); } function upload() { console.log("Request handler 'upload' was called."); } exports.start = start; exports.upload = upload;将一系列请求处理程序通过一个对象来传递,并且需要使用松耦合的方式将这个对象注入到route()函数中 主文件index.js
var server = require("./server"); var router = require("./router"); var requestHandlers = require("./requestHandlers"); var handle = {} handle["/"] = requestHandlers.start; handle["/start"] = requestHandlers.start; handle["/upload"] = requestHandlers.upload; server.start(router.route, handle);server.js
var http = require("http"); var url = require("url"); function start(route, handle) { function onRequest(request, response) { var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); route(handle, pathname); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;route.js
function route(handle, pathname) { console.log("About to route a request for " + pathname); if (typeof handle[pathname] === 'function') { handle[pathname](); } else { console.log("No request handler found for " + pathname); } } exports.route = route;启动应用程序并在浏览器中访问http://localhost:8888/start
8,让请求处理程序作出响应 requestHandler.js
function start() { console.log("Request handler 'start' was called."); return "Hello Start"; } function upload() { console.log("Request handler 'upload' was called."); return "Hello Upload"; } exports.start = start; exports.upload = upload;router.js
function route(handle, pathname) { console.log("About to route a request for " + pathname); if (typeof handle[pathname] === 'function') { return handle[pathname](); } else { console.log("No request handler found for " + pathname); return "404 Not found"; } } exports.route = route;server.js
var http = require("http"); var url = require("url"); function start(route, handle) { function onRequest(request, response) { var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); response.writeHead(200, {"Content-Type": "text/plain"}); var content = route(handle, pathname) response.write(content); response.end(); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;http://localhost:8888/start,浏览器会输出“Hello Start”, http://localhost:8888/upload会输出“Hello Upload”,
9,阻塞与非阻塞 requestHandlers.js
function start() { console.log("Request handler 'start' was called."); function sleep(milliSeconds) { var startTime = new Date().getTime(); while (new Date().getTime() < startTime + milliSeconds); } sleep(10000); return "Hello Start"; } function upload() { console.log("Request handler 'upload' was called."); return "Hello Upload"; } exports.start = start; exports.upload = upload;在第一个浏览器窗口的地址栏中输入http://localhost:8888/start, 但是先不要打开它 在第二个浏览器窗口的地址栏中输入http://localhost:8888/upload, 同样的,先不要打开它! 在第一个窗口中(“/start”)按下回车,然后快速切换到第二个窗口中(“/upload”)按下回车。 start URL加载花了10秒,这和我们预期的一样,/upload URL居然也花了10秒,而它在对应的请求处理程序中并没有类似于sleep()这样的操作! start()包含了阻塞操作。形象的说就是“它阻塞了所有其他的处理工作”。
Node.js是单线程的。它通过事件轮询(event loop)来实现并行操作,尽可能的避免阻塞操作,取而代之,多使用非阻塞操作。
要用非阻塞操作,我们需要使用回调,通过将函数作为参数传递给其他需要花时间做处理的函数。
10,以非阻塞操作进行请求响应 server.js
var http = require("http"); var url = require("url"); function start(route, handle) { function onRequest(request, response) { var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); route(handle, pathname, response); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;router.js
function route(handle, pathname, response) { console.log("About to route a request for " + pathname); if (typeof handle[pathname] === 'function') { handle[pathname](response); } else { console.log("No request handler found for " + pathname); response.writeHead(404, {"Content-Type": "text/plain"}); response.write("404 Not found"); response.end(); } } exports.route = route;requestHandler.js
var exec = require("child_process").exec; function start(response) { console.log("Request handler 'start' was called."); exec("ls -lah", function (error, stdout, stderr) { response.writeHead(200, {"Content-Type": "text/plain"}); response.write(stdout); response.end(); }); } function upload(response) { console.log("Request handler 'upload' was called."); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello Upload"); response.end(); } exports.start = start; exports.upload = upload;11,处理POST请求
requestHandlers.js
function start(response, postData) { console.log("Request handler 'start' was called."); var body = '<html>'+ '<head>'+ '<meta http-equiv="Content-Type" content="text/html; '+ 'charset=UTF-8" />'+ '</head>'+ '<body>'+ '<form action="/upload" method="post">'+ '<textarea name="text" rows="20" cols="60"></textarea>'+ '<input type="submit" value="Submit text" />'+ '</form>'+ '</body>'+ '</html>'; response.writeHead(200, {"Content-Type": "text/html"}); response.write(body); response.end(); } function upload(response, postData) { console.log("Request handler 'upload' was called."); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("You've sent: " + postData); response.end(); } exports.start = start; exports.upload = upload;server.js
var http = require("http"); var url = require("url"); function start(route, handle) { function onRequest(request, response) { var postData = ""; var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); request.setEncoding("utf8"); request.addListener("data", function(postDataChunk) { postData += postDataChunk; console.log("Received POST data chunk '"+ postDataChunk + "'."); }); request.addListener("end", function() { route(handle, pathname, response, postData); }); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;router.js
function route(handle, pathname, response, postData) { console.log("About to route a request for " + pathname); if (typeof handle[pathname] === 'function') { handle[pathname](response, postData); } else { console.log("No request handler found for " + pathname); response.writeHead(404, {"Content-Type": "text/plain"}); response.write("404 Not found"); response.end(); } } exports.route = route;12,处理文件上传 npm install formidable
server.js
var http = require("http"); var url = require("url"); function start(route, handle) { function onRequest(request, response) { var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); route(handle, pathname, response, request); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;router.js
function route(handle, pathname, response, request) { console.log("About to route a request for " + pathname); if (typeof handle[pathname] === 'function') { handle[pathname](response, request); } else { console.log("No request handler found for " + pathname); response.writeHead(404, {"Content-Type": "text/html"}); response.write("404 Not found"); response.end(); } } exports.route = route;requestHandlers.js
var querystring = require("querystring"), fs = require("fs"), formidable = require("formidable"); function start(response) { console.log("Request handler 'start' was called."); var body = '<html>'+ '<head>'+ '<meta http-equiv="Content-Type" content="text/html; '+ 'charset=UTF-8" />'+ '</head>'+ '<body>'+ '<form action="/upload" enctype="multipart/form-data" '+ 'method="post">'+ '<input type="file" name="upload" multiple="multiple">'+ '<input type="submit" value="Upload file" />'+ '</form>'+ '</body>'+ '</html>'; response.writeHead(200, {"Content-Type": "text/html"}); response.write(body); response.end(); } function upload(response, request) { console.log("Request handler 'upload' was called."); var form = new formidable.IncomingForm(); console.log("about to parse"); form.parse(request, function(error, fields, files) { console.log("parsing done"); fs.renameSync(files.upload.path, "/tmp/test.png"); response.writeHead(200, {"Content-Type": "text/html"}); response.write("received image:<br/>"); response.write("<img src='/show' />"); response.end(); }); } function show(response) { console.log("Request handler 'show' was called."); fs.readFile("/tmp/test.png", "binary", function(error, file) { if(error) { response.writeHead(500, {"Content-Type": "text/plain"}); response.write(error + "\n"); response.end(); } else { response.writeHead(200, {"Content-Type": "image/png"}); response.write(file, "binary"); response.end(); } }); } exports.start = start; exports.upload = upload; exports.show = show;