HTTP服务

HTTP服务

HTTP模块是Node的核心模块主要提供了一系列用于网络传输的API,
这些API大都位于比较底层的位置,可以让开发者自由的控制整个HTTP传输过程。

一、创建HTTP服务器

通常使用createServer()方法创建HTTP服务器,下面是一个简单的例子。

1
2
3
4
5
6
var http = require("http")
var servser = http.createServer(function (req,res){
res.writeHead(200,{'Cont-Type':'text/plain'});
res.end("Hello node");
});
server.listen(3000);

上面的代码中,使用createServer方法创建了一个简单的HTTP服务器,该方法返回一个http.server累的实例,
createServer方法包含一个匿名的函数,
该函数有两个参数req和res,
他们是InComingMessage和ServerResponse的实例。
分别表示HTTP的request和response对象,
服务器创完成后,
Node进程开始循环监听3000端口(listen方法实现)。
当浏览器访问localhost:3000端口时,node放回”Hello Node”字符串。

http.server类定义了一系列的事件,在上面的代码中,HTTP请求会触发connection和request事件,
将上面的代码稍微改造,监听来之客户端的事件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
var http = require("http")
var server = http.createServer(function(req,res){
res.writeHead(404, {'Content-Type': 'text/plain'});
res.end("Hello Node!")
});
server.on("connection",function(req,res){
console.log("connected");
})
server.on("request",function(req,res){
console.log("request");
});
server.listen(3000);

当访问http://localhost:3000/时,控制台输出如下:
commectiom
request
request

程序打印出连个request,代表触发了两次request事(其中一次触发favicon.ico请求)。

下面是一个简单的静态文件服务器,只支持文本文件,可以通过浏览器查看服务器文本内容。。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var http = require("http");
var fs = require("fs");
var server = http.createServer(function(req,res){
if(req.url=="/"){
//如果访问路径是localhost:3000,则显示文件列表
var fileList = fs.readdirSync("./");
res.writeHead(200,{"Content-type":"text/plain"});
//将数组转换为字符串返回
res.end(fileList.toString());
}
else{
var path = req.url;
//在路径字符串前加.表示当前目录,避免在*nix系统下访问”/“文件夹
fs.readFile("."+path,function(err,data){
if(err){
//如果该文件不存在,抛出异常
res.end("Internal Error")
throw err;
}
res.writeHead(200,{"Content-type":"text/plain"})
res.end(data);
})
}
})
var port = 3000;
server.listen(port);
console.log("Listening on 3000");

//处理异常
process.on("uncaughtException",function(){
console.log("got error");
})

二、处理HTTP请求

  1. mothod、URL和header

档处理HTTP请求时,最先做的事情就是请求URL、method等信息。
Node将相关信息都封装在一个对象(前面代码req)中,该对象是IncomingMessage的实例。

以获取method和URL为例

1
2
var method = req.method;
var url = req.url;

对于HTTP请求来说,method的值通常是get,post,put,delete,update5个关机键字之一,
以get和post最为常见,url的值为除去网站服务器地址之外的完整值。

1
2
3
例如请求:
http://example.com/index.html?name=Lear
那么URL的值即为index.html?name=Lear。
  1. header
1
2
3
4
5
6
7
8
http header通常为以下形式:
{
'conent-length':'123',
'conent-type':'text/plain',
'connection':'keeo-alive',
'host':'mysite.com',
'accept':'*/*'
}

Node 获取HTTP header信息也很简单,header是一个json对象可已对属性名进行单独索引:

1
2
var haeders = req.headers;
var userAgent = haeders['user-agent'];
  1. request body
    Node使用stream来处理HTTP请求,这个stream注册了data和end两个事件。
    下面的这个段代码通常获取完整的HTTP内容体,在Buffer的一节我们已经提到过了。
1
2
3
4
5
6
var body = [];
request.on('data',function(chunk){
body.push(chunk);
}).on('end',function (){
body = Buffer.concat(body).toString();
});

目前我们没有提到response对象,该对象是ServerResponse的一个实例,并且实现了一个writeStream,这个在介绍到Strean时会讲到,这里就不多说了。

三、Response对象

  1. 设置statusCode
    转态的设置在web开发中常常被忽略,在Node中如果开发者不手动设置,那么状态码的值会默认为200。
    但这个值并不适合所有场景,另一个常用的状态是404,表示服务去没有对应的资源。
    Web开发中如果遇到非法的路径访问,通常会返回一个404 nofound的页面,但实际上,即使开发者返回一个200的状态码,也能将对应的页面返回,因此状态码的设置通常是最佳实践,而非强制编码规范。

  2. 设置response header
    通过setHeader方法设置response的头部信息。

1
2
response.setHeader('Conent-Type','aplication/json');
response.setHeader('X-Prowered-By','bacon');

setHeader方法可以设置response header 单个属性的内容,如果想要一次性设置所有属性响应头和状态码,可以使用writeHead方法。

respone.writeHead
writeHead方法用于定义HTTP现应,包括状态码等一系列属性,
下面的例子会同时设置状态码和多个和多个字段。

1
2
respone.writeHead(200,{'Conent-Length':Buffer.byteLength(body),
'Content-Type':'text/palin'});

调用该方法后,服务器向客户端发送HTTP相应头,后面通常会跟着调用res.write等方法,响应头不可重复发送。
当调用end方法的时候也会调用writeHead,此时statesCode会自动设置成200。

  1. response body
    response对象是一个WritableStream实例,可以直接用write方法写入,写入后在调用end方法将stream发送到客户端。
1
2
3
4
5
6
response.write('<html>');
response.write('<body>'');
response.write('<h1>Hello World!</h1>');
response.write('</body>');
response.write('</html>');
response.end();

不过这样会显得有些繁琐,也可以直接将response body作为 end方法的参数进行返回。

1
2
3
4
5
6
response.end('
<html>
<body>
<h1>Hello World</h1>
</body>
</html>');
  1. response.end
    end 方法在每个HTTP请求最后都会被调用,end方法支持一个字符串或者Buffer
    作为参数,可以在指定HTTP请求的最后返回的数据,该数据会在浏览器页面上显示出来;如果定义了回调方法,那么会在end返回后调用。
1
2
3
res.end("Hello Node",function(){
conlose.log("http cycle end");
});

四、上传数据

在实际的业务开发中,用户除了接收数据外,往往还有上传数据的需求,例如表单提交,提交文件等。
前面的小点只处理了头部信息,头部之外的类容(body部分)需要开发者自己解析,否则这部分类容就会被Node程序丢弃。
在传统Web开发中,最常用的HTTP请求只有post和get两种,get请求的报文内容很简单,只有请求和请求头部;post请求由于要上传数据,因此需要包含请求体的内容,有两个相关属性经常被用到,分别是conten-type和content-length。

1
2
3
4
5
6
可以通过req.method属性来判断请求类型:
var HTTP= require('http');
var server =http.careateServer(function(req,res){
if(req.method == "get"){}
if(req.method == "post"){}
});
  1. 提交表单
    表单的提交时post请求最常见的情景之一。
1
2
3
4
5
6
7
<form actiom="/login" method ="post" id ="form1">
<input type="text" name="username" id="username"/>
<br />
<input type="password" name="password" id="password"/>
<br />
<input type="submit" name="submit" id ="submit"/>
</form>

上面HTML代码定义了一个简单的form表单,单击submit按钮后将会将整个表当提交”/login”路径下。

下面是Node服务器代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var http = require("http");
var fs = require("fs");
var server = http.createServer(function(req,res){
if(req.url == "/login"){
swich(req.method){
case "GET":
//使用流来加载login.html
fs.creatReadStream("/login.html").pipe(res);
break;
case "POST":
dealPost(req,res);//自定义处理方法
break;
default :
console.log("other request");//其他请求类型
}
}else{
res.writeHead(302,{
'Location':'/login'

});
res.end();//将所有的url访问都转到/login路径
}
})
server.listen(3000);
  1. 使用post上传
    首先构建一个用于上传文件的表单。
1
2
3
4
5
<form actiom="/upload" method ="post" enctype="multipart/form-data">
<input type="file" name="file" />
<br />
<input type="submit" name="submit" id ="submit"/>
</form>

服务器上处理上传文件

1
2
3
4
5
6
7
8
9
10
11
12
function dealUpload(req,res){
var form = new formidable.IncomingForm();//创建formidable.IncomingForm对象
form.keepExtension = true;//保留原有的扩展名
form.uploadDir= _dirname;//上传目录为当前目录
form.parse(req,function (err, fileds, files){
if (err){thow err;}
console.log(fields);//{submit:"submit"}
console.log(files);
res.writeHead(200,{"content-type":'text/plain'});
res.end("upload finished");
})
}

如果想要获取file对象中的一些属性,可以通过

1
2
files.file.[]
[]参数名称(键值对的形式)

五、HTTP客户端服务

HTTP模块除了能在服务端处理请求之外,还可以作为客户端向服务器发起请求,例如通过http.get请求通过post方法上传文件等。

HTTP.GET声明如下:

1
2
3
4
http.get(options[,callback])#
options<object>
callback<Functiom>
Rerurns:<Http.ClientREquest>

发起一个请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var http = require("http");
http.get("http://blockchain.info/ticker",function(res){
var statusCode = res.statusCode;
if(statusCode = 200){
//buffer一节已经提到过,试着修改下面的代码
var result = "";
res.on("data",function(data){
result+=data;
})
res.on("end",function(){
console.log(result.toString());
});
res.on("error",function(e){
console.log(e.message);
})
}
})

上面代码向http://blockchain.info/ticke发起了一个get请求。