4.Node学习:核心模块之二(http、url、querystring、net)

核心模块由一些精简高效的库组成,提供了基本的API

一. http模块

Node.js出现的目的就是编写高性能Web服务器,因此http模块提供了一个高效的HTTP服务器和一个简易的HTTP客户端

http.Server 是一个基于事件的HTTP 服务器,它的核心由Node.js 下层C++ 部分实现,而接口由JavaScript 封装,兼顾了高性能与简易性

http.request 则是一个HTTP 客户端,用于向HTTP 服务器发起请求

1. HTTP服务器

http.Server 是 http 模块中的 HTTP 服务器对象,用 Node.js 做的所有基于 HTTP 协议的系统都是基于 http.Server 实现的。它提供了一套封装级别很低的 API,仅仅是流控制和简单的消息解析,所有的高层功能都要通过它的接口来实现

官方示例抢先看

var http = require('http');
http.createServer(function (req, res) {
	res.writeHead(200, {'Content-Type': 'text/plain'});
	res.end('Hello World\n');
}).listen(1337);
console.log('Server running at http://127.0.0.1:1337/');

http.createServer()创建了一个 http.Server 的实例对象,将一个函数作为 HTTP 请求处理函数。这个函数接受两个参数,分别是请求对象( req )和响应对象( res )。在函数体内, res 显式地写回了响应代码 200 (表示请求成功),指定响应头为'Content-Type': 'text/html' ,然后写入响应体 '<h1>Node.js</h1>' ,通过 res.end结束并发送。最后该实例还调用了 listen 函数,启动服务器并监听 3000 端口

注:在终端中运行这个脚本时,并不会结束后立即退出,而是一直等待,直到按下Ctrl + C 才会结束。这是因为listen 函数中创建了事件监听器,使得Node.js进程不会退出事件循环

注:在Linux系统下,监听1024以下端口需要root权限,有两种方式可以解决

一种方式是使用sudo命令运行Node.js(一般推荐这种方式,这样可以保证仅为有需要的JS脚本提供root权限)

sudo node server.js

另一种方式是使用chmod +s 命令让Node.js总是以root权限运行(因为这种方式让任何JS脚本都有了root权限,不太安全)

sudo chown root /usr/local/bin/node
sudo chmod +s /usr/local/bin/node

函数在Node中的体现

在JavaScript中,一个函数可以作为另一个函数的参数传递。我们可以先定义一个函数,然后传递,也可以在传递参数的地方直接定义函数。

Node.js中函数的使用与Javascript类似

function say(word) {
	console.log(word);
}
function execute(someFunction, value) {
	someFunction(value);
}
execute(say, "Hello");

我们把 say 函数作为execute函数的第一个参数进行了传递。这里传递的不是 say 的返回值,而是 say 本身!

这样一来, say 就变成了execute 中的本地变量 someFunction ,execute可以通过调用 someFunction() (带括号的形式)来使用 say 函数。

当然,因为 say 有一个变量, execute 在调用 someFunction 时可以传递这样一个变量。

我们可以把一个函数作为变量传递。但是我们不一定要绕这个"先定义,再传递"的圈子,我们可以直接在另一个函数的括号中定义和传递这个函数:

function execute(someFunction, value) {
	someFunction(value);
}
execute(function(word){ console.log(word) }, "Hello");

我们在 execute 接受第一个参数的地方直接定义了我们准备传递给 execute 的函数。用这种方式,我们甚至不用给这个函数起名字,这也是为什么它被叫做匿名函数 。

带着这些知识,我们再来看看我们简约而不简单的HTTP服务器:

var http = require("http");
http.createServer(function(request, response) {
	response.writeHead(200, {"Content-Type": "text/plain"});
	response.write("Hello World");
	response.end();
}).listen(8888);

现在它看上去应该清晰了很多:我们向 createServer 函数传递了一个匿名函数。

用这样的代码也可以达到同样的目的:

var http = require("http");
function onRequest(request, response) {
	response.writeHead(200, {"Content-Type": "text/plain"});
	response.write("Hello World");
	response.end();
}
http.createServer(onRequest).listen(8888);

1). http.Server的事件

http.Server 是一个基于事件的HTTP 服务器,所有的请求都被封装为独立的事件,只需要对它的事件编写响应函数即可实现HTTP 服务器的所有功能

包括以下几个事件

request事件

当客户端请求到来时,该事件被触发,回调函数中提供两个参数 req 和res,分别是http.ServerRequest 对象和 http.ServerResponse 的实例,表示请求和响应信息

connection事件

当TCP 连接建立时,该事件被触发,提供一个参数 socket,为net.Socket 的实例。connection 事件的粒度要大于 request,因为客户端在Keep-Alive 模式下可能会在同一个连接内发送多次请求

close事件

当服务器关闭时,该事件被触发。注意不是在用户连接断开时

除此之外还有 checkContinue、upgrade、clientError 事件

在这些事件中,最常用的就是 request了,因此http 提供了一个简式写法

http.createServer([requestListener])

功能是创建一个HTTP 服务器并将requestListener 作为 request 事件的监听函数,这也是官方示例中使用的方法

事实上它显式的实现方法是

var http = require('http'); 
var server = new http.Server(); 
server.on('request', function(req, res) { 
	res.writeHead(200, {'Content-Type': 'text/html'}); 
	res.write('<h1>Node.js</h1>'); 
	res.end('<p>Hello World</p>'); 
}); 
server.listen(3000); 
console.log("HTTP server is listening at port 3000.");

下面着重讲解一下,request事件发生时,回调函数的两个参数req和res,分别对应 http.ServerRequest对象和http.ServerResponse对象

2). http.ServerRequest对象

http.ServerRequest 是HTTP 请求的信息,一般由http.Server 的 request 事件发送,作为回调函数的第一个参数传递,通常简称 request 或 req

HTTP 请求一般可以分为两部分:请求头(Request Header)和请求体(Requset Body)

以上内容由于长度较短都可以在请求头解析完成后立即读取。而请求体可能相对较长,需要一定的时间传输,因此 http.ServerRequest 提供了以下3个事件用于控制请求体传输

data事件:当请求体数据到来时,该事件被触发。该事件提供一个参数 chunk,表示接收到的数据。如果该事件没有被监听,那么请求体将会被抛弃。该事件可能会被调用多次。

end事件 :当请求体数据传输完成时,该事件被触发,此后将不会再有数据到来。

close事件: 用户当前请求结束(或中断)时,该事件被触发。不同于 end,如果用户强制终止了传输,也会调用close

ServerRequest 提供一些属性

complete 客户端请求是否已经发送完成(true、false)

httpVersion HTTP 协议版本,通常是1.0 或1.1

method HTTP 请求方法,如 GET、POST、PUT、DELETE 等

url 原始的请求路径,例如/static/image/x.jpg 或/user?name=byvoid

headers HTTP 请求头

trailers HTTP 请求尾(不常见)

connection 当前HTTP 连接套接字,为net.Socket 的实例

socket connection属性的别名

client client属性的别名

可以看到http.ServerRequest的属性中并没有类似于PHP中的$GR或者$POST,那么如何接收客户端的表单请求呢?

由于 GET 请求直接被嵌入在路径中, URL是完整的请求路径,包括了 ? 后面的部分,因此你可以手动解析后面的内容作为 GET请求的参数

Node.js 的 url 模块中的 parse 函数提供了这个功能

var http = require('http');
var url = require('url');
var util = require('util');
http.createServer( function(req, res) {
	res.writeHead( 200, {'Content-Type': 'text/plain'});
	res.end(util.inspect(url.parse(req.url, true)));
}).listen( 3000);

在浏览器中访问 http://127.0.0.1:3000/user?name=byvoid&[email protected],我们可以看到浏览器返回的结果

{
	search: '?name=byvoid&[email protected]',
	query: { name: 'byvoid', email: '[email protected]' },
	pathname: '/user',
	path: '/user?name=byvoid&[email protected]',
	href: '/user?name=byvoid&[email protected]'
}

通过 url.parse,原始的 path 被解析为一个对象,其中 query 就是我们所谓的 GET
请求的内容,而路径则是 pathname

GET 请求把所有的内容编码到访问路径中, POST 请求的内容全部都在请求体中。http.ServerRequest 并没有一个属性内容为请求体,原因是等待请求体传输可能是一件耗时的工作,所以 Node.js 默认是不会解析请求体的,当你需要的时候,需要手动来做

var http = require('http');
var querystring = require('querystring');
var util = require('util');
http.createServer( function(req, res) {
	var post = '';	
	req.on('data', function(chunk) {
		post += chunk;
	});
	req.on('end', function() {
		post = querystring.parse(post);
		res.end(util.inspect(post));
	});
}).listen( 3000);

上面代码并没有在请求响应函数中向客户端返回信息,而是定义了一个 post 变量,用
于在闭包中暂存请求体的信息。通过 req 的 data 事件监听函数,每当接受到请求体的数据,就累加到 post 变量中。在 end 事件触发后,通过 querystring.parse 将 post 解析为真正的 POST 请求格式,然后向客户端返回。

注:不要在真正的生产应用中使用上面这种简单的方法来获取 POST 请求,因为它有严重的效率问题和安全问题,这只是示例

3). http.ServerResponse对象

http.ServerResponse 是返回给客户端的信息,决定了用户最终能看到的结果。它也是由 http.Server 的 request 事件的回调函数中发送,作为第二个参数传递,一般简称为
response 或 res

response.writeHead(statusCode, [headers])

向请求的客户端发送响应头。statusCode 是HTTP 状态码,如200 (请求成功)、404 (未找到)等。headers 是一个类似关联数组的对象,表示响应头的每个属性。该函数在一个请求内最多只能调用一次,如果不调用,则会自动生成一个响应头

response.write(data, [encoding])

向请求的客户端发送响应内容。data 是一个 Buffer 或字符串,表示要发送的内容。如果 data 是字符串,那么需要指定encoding 来说明它的编码方式,默认是utf-8。在response.end 调用之前,response.write 可以被多次调用。

response.end([data], [encoding])

结束响应,告知客户端所有发送已经完成。当所有要返回的内容发送完毕的时候,该函数 必须 被调用一次。它接受两个可选参数,意义和response.write 相同。如果不调用该函数,客户端将永远处于等待状态

2. HTTP客户端

Node.js除了可以作为服务器之外,还可以作为客户端,去向服务器发出HTTP请求

..........

3. supervisor模块

在开发Node.js 实现的HTTP 应用时会发现,无论你修改了代码的哪一部份,都必须终止Node.js 再重新运行才会奏效。这是因为Node.js 只有在第一次引用到某部份时才会去解析脚本文件,以后都会直接访问内存,避免重复载入

Node.js的这种设计虽然有利于提高性能,却不利于开发调试,因为我们在开发过程中总是希望修改后立即看到效果,而不是每次都要终止进程并重启。supervisor 模块可以帮助你实现这个功能,它会监视你对代码的改动,并自动重启Node.js

使用方法很简单,首先使用npm 全局安装supervisor

npm install -g supervisor 

如果你使用的是Linux 或Mac,直接键入上面的命令很可能会有权限错误。原因是npm 需要把supervisor 安装到系统目录,需要管理员授权,可以使用 sudo npm install -g supervisor 命令来安装。

接下来,使用supervisor 命令启动app.js

supervisor app.js 

当代码被改动时,运行的脚本会被终止,然后重新启动,解决开发中的调试问题

二. url模块

url模块包含了 解析URL的实用函数

1. 引入模块

作为核心模块,引入很简单

var url = require('url');

不同的URL包含了不同的字段,看一个例子

http://user:[email protected]:8080/p/a/t/h?query=string#hash

href:所解析的完整的URL,协议名和主机名都转换为小写

http://user:[email protected]:8080/p/a/t/h?query=string#hash

protocol:请求的协议,小写

http:

auth:URL中身份验证信息部分

user:pass

host: URL主机名,小写, 包括端口信息

host.com:8080

hostname:主机的主机名部分, 小写

host.com

port::主机的端口号部分

8080

pathname:URL的路径部分,位于主机名(或者端口)之后,查询字符串之前(包括最初的斜线)

/p/a/t/h

search:URL 的“查询字符串”部分,包括开头的问号

?query=string

path:pathname 和 search 连在一起

/p/a/t/h?query=string

query:查询字符串中的参数部分(问号后面部分字符串),或者使用 querystring.parse() 解析后返回的对象

query=string

or
{'query':'string'}

hash::URL 的 “#” 后面部分(包括 # 符号)

返回#hash

2. API

url.parse()方法

该方法用于将URL字符串解析为一个对象,接收三个参数

第一个参数,字符串,必须,表示要解析的URL

第二个参数,布尔值,可选,表示是否使用 querystring 模块来解析 URL 中的查询字符串部分,默认为 false,为true时查询字符串将以对象形式表示

第三个参数,布尔值,可选,表示是否把诸如 //foo/bar 这样的URL解析为 { host: 'foo', pathname: '/bar' } 而不是 { pathname: '//foo/bar' },默认为 false

var http = require('http');
var url = require('url');
http.createServer(function(req,res){
	var urlObj = url.parse(req.url);
	//可以根据需要取得url的任意部分
	console.log(urlObj.host);
	console.log(urlObj.protocol);
	.........
}).listen(3000);

url.format(urlObj)方法

该方法接收一个被解析的URL 对象,返回格式化后的 URL 字符串(url.parse()方法的反操作)

url.resolve(from, to)

给定一个基础URL路径,和一个href URL路径,拼接它们

url.resolve('/one/two/three', 'four')           '/one/two/four'
url.resolve('http://ex.com/', '/one')           'http://ex.com/one'
url.resolve('http://ex.com/one', '/two')     'http://ex.com/two'

三. querystring模块

querystring模块包含了处理查询字符串的实用函数,用于实现URL参数字符串与参数对象的互相转换

1. 引入模块

作为核心模块,引入很简单

var querystring  = require('querystring');

2. API

querystring.stringify()方法

该方法用于将参数对象序列化为一个查询字符串,接收四个参数

第一个参数,对象,必须,表示要序列化的参数对象

第二个参数,字符串,可选,表示参数字符串的分隔符,替代"&"符

第三个参数,字符串,可选,表示参数字符串的分配符,替代"="符

第四个参数,对象,可选,表示字符串的编码方式

var querystring  = require('querystring');
querystring.stringify({ a: '1', b: ['2', '3'], c: '' })
返回'a=1&b=2&b=3&c='
querystring.stringify({ a: '1', b: '2'},"-","+")
返回'a+1-b+2
querystring.stringify({ a: '中文', b: 'car' }, null, null,{ encodeURIComponent: gbkEncodeURIComponent })
返回'a=%D6%D0%CE%C4&b=car'

querystring.parse()方法

该方法用于将一个查询字符串解析为对象

第一个参数,字符串,必须,表示要解析的查询字符串

第二个参数,字符串,可选,表示参数字符串的分隔符,替代"&"符

第三个参数,字符串,可选,表示参数字符串的分配符,替代"="符

第四个参数,对象,可选,表示字符串的编码方式

querystring.parse('foo=bar&baz=qux&baz=quux&corge')
返回{ foo: 'bar', baz: ['qux', 'quux'], corge: '' }

querystring.escape()方法

用于参数编码

querystring.unescape()方法

用于参数解码

四. net模块

net模块提供了异步网络封装,包含了一些创建TCP服务器和客户端的接口,使用net模块能很快的开发出基于TCP的服务端和客户端

1. 引入模块

作为核心模块,引入很简单

var net  = require('net');

2. 创建TCP服务器

创建一个新的TCP服务,使用createServer()方法,该方法接收两个参数,返回一个服务器实例对象

var tcpServer = net.createServer([options],[connectionListener])

第一个参数,可选,表示配置项,默认 { allowHalfOpen: false }

第二个参数,可选,表示回调函数(”TCP连接事件“发生时的监听器)

var net = require('net');
var server = net.createServer(function(c){
	console.log('server connected');
	c.on('end', function() {
		console.log('server disconnected');
	});
	c.write('hello\r\n');
	c.pipe(c);
});
server.listen(8124, function() { //'listening' listener
	console.log('server bound');
});

3. 创建TCP服务器2

除了上面的方法,还可以使用

net.connect(options, [connectionListener])
net.createConnection(options, [connectionListener])

创建一个新的TCP服务器,并且返回创建的socket对象(而不是作为回调函数的参数传入了)

第一个参数,必须,表示配置项,通常包含port、host、localAddress属性

第二个参数,可选,表示回调函数(”TCP连接事件“发生时的监听器)

注:Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求

var net = require('net');
var client = net.connect({port: 8124},function(){
	console.log('client connected');
	client.write('world!\r\n');
});
client.on('data', function(data) {
	console.log(data.toString());
	client.end();
});
client.on('end', function() {
	console.log('client disconnected');
});

4. 创建TCP服务器3

上面的方法,还可以这样使用(参数形式变化)

net.connect(port, [host], [connectListener])
net.createConnection(port, [host], [connectListener])

第一个参数,必须,表示监听的端口号

第二个参数,可选,表示坚挺的主机名(默认为localhost)

第三个参数,可选,表示回调函数(”TCP连接事件“发生时的监听器)

5. socket对象

remoteAddress属性

返回远程连接的IP地址

remotePort属性

返回远程连接的端口号

localAddress属性

返回本地的IP地址

localPort属性

返回本地的端口号

socket.bytesRead属性

返回所接收的字节数

socket.bytesWritten属性

返回所发送的字节数

.对象方法

......

对象事件

.......

你可能感兴趣的:(String)