Node.js HTTP 使用详解
对于初学者有没有发觉在查看Node.js官方API的时候非常简单,只有几个洋文描述两下子,没了,我第一次一口气看完所以API后,对于第一个示例都有些懵,特别是参数里的request和response,究竟是如何通过参数工作的,如果并发量大如何确保每个人访问和提交的数据不干扰等等。都没有教你具体如何在开发中使用,如何着手写代码,给你一个Event 'close',只说了下在服务器关闭时触发,完了。如果没有了解EventEmitter的核心事件,可能还真不知道如何抒写代码并在开发中真正使用。而http server创建的服务对象已经继承了EventEmitter,所以可以直接使用on进行监听即可。学学util包中的inherits是如何继承EventEmitter的就应该略知一二了。
在官方文档的API中有服务器对象和回调函数参数返回参数的对象,response和request对象各有两种不同。一种是server级别的一种是client级别的。
关于HTTP部分大致分为如下的重要点:
直接通过http对象使用的有:
一、http.STATUS_CODES
二、http.createServer
三、http.request(http.ClientRequest)
四、http.get
五、http.globalAgent
作为回调参数使用的对象有:
http: { STATUS_CODES: { '100': 'Continue', '101': 'Switching Protocols', '102': 'Processing', '200': 'OK', '201': 'Created', '202': 'Accepted', '203': 'Non-Authoritative Information', '204': 'No Content', '205': 'Reset Content', '206': 'Partial Content', '207': 'Multi-Status', '300': 'Multiple Choices', '301': 'Moved Permanently', '302': 'Moved Temporarily', '303': 'See Other', '304': 'Not Modified', '305': 'Use Proxy', '307': 'Temporary Redirect', '400': 'Bad Request', '401': 'Unauthorized', '402': 'Payment Required', '403': 'Forbidden', '404': 'Not Found', '405': 'Method Not Allowed', '406': 'Not Acceptable', '407': 'Proxy Authentication Required', '408': 'Request Time-out', '409': 'Conflict', '410': 'Gone', '411': 'Length Required', '412': 'Precondition Failed', '413': 'Request Entity Too Large', '414': 'Request-URI Too Large', '415': 'Unsupported Media Type', '416': 'Requested Range Not Satisfiable', '417': 'Expectation Failed', '418': 'I\'m a teapot', '422': 'Unprocessable Entity', '423': 'Locked', '424': 'Failed Dependency', '425': 'Unordered Collection', '426': 'Upgrade Required', '428': 'Precondition Required', '429': 'Too Many Requests', '431': 'Request Header Fields Too Large', '500': 'Internal Server Error', '501': 'Not Implemented', '502': 'Bad Gateway', '503': 'Service Unavailable', '504': 'Gateway Time-out', '505': 'HTTP Version Not Supported', '506': 'Variant Also Negotiates', '507': 'Insufficient Storage', '509': 'Bandwidth Limit Exceeded', '510': 'Not Extended', '511': 'Network Authentication Required' } }
测试用例:
var http = require('http'); http.createServer(function(req,res){ var status = req.url.substr(1); if( ! http.STATUS_CODES[status]) { status = '404'; } res.writeHeader(status,{'Content-Type':'text/plain'}); res.end(http.STATUS_CODES[status]); }).listen(3000);
测试连接:http://localhost:3000/500 结果输出 Internal Server Error
二、http.createServer
http.createServer是创建一台web服务器的关键所在,是处理请求和回应的主函数出口和出口,我们把http.createServer创建的服务对象定义为server.代码如下。
/** * Created by Administrator on 14-4-29. */ var http = require('http'); /** * 创建服务器的两种写法,第一种写法如下 * 由于server已经继承了EventEmitter的事件功能,所以可以使用高级函数编写方式监控事件 * @param {Function} request event */ var server = http.createServer(function(req,res) { //这里的req为http.serverRequest res.writeHeader(200,{'Content-Type':'text/plain'}); res.end('hello world'); }); /** * 说明:创建服务器的第二种写法 * 有关server对象的事件监听 * @param {Object} req 是http.IncomingMessag的一个实例,在keep-alive连接中支持多个请求 * @param {Object} res 是http.ServerResponse的一个实例 */ var server = new http.Server(); server.on('request',function(req,res){ res.writeHeader(200,{'Content-Type':'text/plain'}); res.end('hello world'); }); /** * 说明:新的TCP流建立时出发。 socket是一个net.Socket对象。 通常用户无需处理该事件。 * 特别注意,协议解析器绑定套接字时采用的方式使套接字不会出发readable事件。 还可以通过request.connection访问socket。 * @param {Object} socket */ server.on('connection',function(socket){}); /** * 源API: Event: 'close' * 说明:关闭服务器时触发 */ server.on('close',function(){}); /** * 说明:每当收到Expect: 100-continue的http请求时触发。 如果未监听该事件,服务器会酌情自动发送100 Continue响应。 * 处理该事件时,如果客户端可以继续发送请求主体则调用response.writeContinue, 如果不能则生成合适的HTTP响应(例如,400 请求无效) * 需要注意到, 当这个事件触发并且被处理后, request 事件将不再会触发. * @param {Object} req * @param {Object} req */ server.on('checkContinue',function(req,res){}); /** * 说明:如果客户端发起connect请求,如果服务器端没有监听,那么于客户端请求的该连接将会被关闭 * @param {Object} req 是该HTTP请求的参数,与request事件中的相同。 * @param {Object} socket 是服务端与客户端之间的网络套接字。需要自己写一个data事件监听数据流 * @param {Object} head 是一个Buffer实例,隧道流的第一个包,该参数可能为空。 */ server.on('connect',function(req,socket,head){}); /** * 说明:这个事件主要是对HTTP协议升级为其他协议后的事件监听,如果服务器端没有监听,那么于客户端请求的该连接将会被关闭 * @param {Object} req 是该HTTP请求的参数,与request事件中的相同。 * @param {Object} socket 是服务端与客户端之间的网络套接字。需要自己写一个data事件监听数据流 * @param {Object} head 是一个Buffer实例,升级后流的第一个包,该参数可能为空。 */ server.on('upgrade',function(req,socket,head){}); /** * 说明:如果一个客户端连接触发了一个 'error' 事件, 它就会转发到这里 * @param {Object} exception * @param {Object} socket */ server.on('clientError',function(exception,socket){}); /** * 源API:server.listen(port, [hostname], [backlog], [callback]) * 说明:监听一个 unix socket, 需要提供一个文件名而不是端口号和主机名。 * @param {Number} port 端口 * @param {String} host 主机 * @param {Number} backlog 等待队列的最大长度,决定于操作系统平台,默认是511 * @param {Function} callback 异步回调函数 */ //server.listen(3000,'localhost',100,function(){}); /** * 源API:server.listen(path, [callback]) * 说明:启动一个 UNIX 套接字服务器在所给路径 path 上监听连接。 * 可能用处:多路径或渠道数据来源监听分隔 * @param {String} path * @param {Function} callback */ //server.listen('path',function(){}) /** * 源API:server.listen(handle, [callback]) * 说明:Windows 不支持监听一个文件描述符。 * @param {Object} handle 变量可以被设置为server 或者 socket * @param {Function} callback */ //server.listen({},function(){}); /** * 说明:最大请求头数目限制, 默认 1000 个. 如果设置为0, 则代表不做任何限制. * @type {number} */ server.maxHeadersCount = 1000; /** * 源API:server.setTimeout(msecs, callback) * 说明:为套接字设定超时值。如果一个超时发生,那么Server对象上会分发一个'timeout'事件,同时将套接字作为参数传递。 * 设置为0将阻止之后建立的连接的一切自动超时行为 * @param {Number} msecs * @param */ server.setTimeout(1000,function(){}); /** * 说明:一个套接字被判断为超时之前的闲置毫秒数。 默认 120000 (2 分钟) * @type {number} */ server.timeout = 120000; /** * 说明:这里的主机将是本地 * @param {Number} port 端口 * @param {Function} callback 异步回调函数 */ server.listen(3000,function(){ console.log('Listen port 3000'); });
三 、http.request
http 模块提供了两个函数 http.request 和 http.get,功能是作为客户端向 HTTP服务器发起请求。http.request(options, callback) 发起 HTTP 请求。接受两个参数,option 是一个类似关联数组的对象,表示请求的参数,callback 是请求的回调函数。option常用的参数如下所示。 http.request 返回一个 http.ClientRequest 的实例。
/** * Created by Administrator on 14-4-30. */ var http = require('http'); var server = http.createServer(function(req,res){ }).listen(3000); /** * 参数配置 * @type {{hostname: string, port: number, method: string, path: string,handers: {}}} * host:请求的服务器域名或者IP地址 * port:端口 * method:请求方式有POST,GET,INPUT,DELETE,CONNECT,默认为GET * path:请求地址,可包含查询字符串以及可能存在的锚点。例如'/index.html?page=12' * handers: 一个包含请求头的对象。 */ var options = { hostname : 'www.google.com', port : 80, method : 'POST', path : '/upload', handers:{} }; /** * 如下特别的消息头应当注意: * 发送'Connection: keep-alive'头部将通知Node此连接将保持到下一次请求。 * 发送'Content-length'头将使默认的分块编码无效。 * 发送'Expect'头部将引起请求头部立即被发送。 * 通常情况,当发送'Expect: 100-continue'时,你需要监听continue事件的同时设置超时。参见RFC2616 8.2.3章节以获得更多的信息。 */ /** * 说明:官方给出的例子 * 应用场景:模拟客服端请求服务器,是一个HTTP 客户端工具,用于向 HTTP 服务器发起请求。 * @param {Object} options * @param {Function} callback */ var req = http.request(options,function(res){ console.log(res); console.log('STATUS:' + res.statusCode); console.log('HEADERS:' + JSON.stringify(res.headers)); res.setEncoding('utf8'); res.on('data',function(chunk){ console.log('BODY' + chunk); }); }); req.on('response',function(){ }); req.on('connect',function(){ }); req.on('socket',function(){ }); req.on('upgrade',function(){ }); req.on('continue',function(){ }) //如果在请求过程中出现了错误(可能是DNS解析、TCP的错误、或者HTTP解析错误),返回的请求对象上的'error'的事件将被触发。 req.on('error',function(e){ console.log(e.message); }); /** * 源API:request.write(chunk, [encoding]) * 说明:发送正文中的一块。用户可以通过多次调用这个方法将请求正文以流的方式发送到服务器。此种情况建议在建立请求时使用['Transfer-Encoding', 'chunked']请求头。 * @param {Object or String} chunk 参数chunk应当是一个整数数组或字符串。 * @param {String} encoding 参数encoding是可选的,仅在chunk为字符串时可用。 */ req.write('data\n'); /** * 源API:request.end(chunk, [encoding]) * 说明:完成本次请求的发送。如果正文中的任何一个部分没有来得及发送,将把他们全部刷新到流中。如果本次请求是分块的,这个函数将发出结束字符'0\r\n\r\n'。如果使用参数data,就等于在调用request.write(data, encoding)之后紧接着调用request.end()。 * @param {Object or String} chunk 参数chunk应当是一个整数数组或字符串。 * @param {String} encoding 参数encoding是可选的,仅在chunk为字符串时可用。 * example: req.end(),req.end('data\n'),req.end('data','utf8'),req.end(chunk) */ req.end(); /** * 阻止一个请求。(v0.3.8中新增的方法。) */ req.abort(); /** * 源API:request.setTimeout(timeout, [callback]) * 说明:一旦给这个请求分配的是一个socket时此函数会被调用 * @param {Number} timeout 毫秒 * @param {Function} callback 回到函数 */ req.setTimeout(1000,function(){}); /** * 源API :request.setNoDelay([noDelay]) * 说明:默认有一定的延迟,设置为0表示无延迟 * @param {Number} noDelay */ req.setNoDelay(0) /** * 源API:request.setSocketKeepAlive([enable], [initialDelay]) * 类似同上 */
四、http.get
http.get(options, callback) http 模块还提供了一个更加简便的方法用于处理GET请求:http.get。它是 http.request 的简化版,唯一的区别在于http.get自动将请求方法设为了 GET 请求,同时不需要手动调用 req.end()。
/** * Created by Administrator on 14-4-30. */ var http = require('http'); http.createServer(function(req,res){ }).listen(3000); /** * 说明:由于大部分请求是不包含正文的GET请求,Node提供了这个方便的方法。与http.request()唯一的区别是此方法将请求方式设置为GET,并且自动调用req.end()。 * 应用:服务器端测试客服端请求调试等 * @param {String} url 有效地址 * @param {Function} callback */ http.get('http://www.baidu.com/index.html',function(res){ console.log('get response Code :' + res.statusCode); }).on('error',function(e){ console.log("Got error: " + e.message); })