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请求
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 ,update 5个关机键字之一, 以get和post最为常见,url的值为除去网站服务器地址之外的完整值。
1 2 3 例如请求: http://example.com/index.html?name=Lear 那么URL的值即为index.html?name=Lear。
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'];
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对象
设置statusCode 转态的设置在web开发中常常被忽略,在Node中如果开发者不手动设置,那么状态码的值会默认为200。 但这个值并不适合所有场景,另一个常用的状态是404,表示服务去没有对应的资源。 Web开发中如果遇到非法的路径访问,通常会返回一个404 nofound的页面,但实际上,即使开发者返回一个200的状态码,也能将对应的页面返回,因此状态码的设置通常是最佳实践,而非强制编码规范。
设置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。
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>');
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"){} });
提交表单 表单的提交时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);
使用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请求。