07月31, 2016

node学习笔记(3)

node学习笔记第三篇。

1.http

Http协议与TCP协议简单理解

TCP协议对应于传输层,而HTTP协议对应于应用层,从本质上来说,二者没有可比性。Http协议是建立在TCP协议基础之上的,当浏览器需要从服务器获取网页数据的时候,会发出一次Http请求。Http会通过TCP建立起一个到服务器的连接通道,当本次请求需要的数据完毕后,Http会立即将TCP连接断开,这个过程是很短的。所以Http连接是一种短连接,是一种无状态的连接。所谓的无状态,是指浏览器每次向服务器发起请求的时候,不是通过一个连接,而是每次都建立一个新的连接。如果是一个连接的话,服务器进程中就能保持住这个连接并且在内存中记住一些信息状态。而每次请求结束后,连接就关闭,相关的内容就释放了,所以记不住任何状态,成为无状态连接。

随着时间的推移,html页面变得复杂了,里面可能嵌入了很多图片,这时候每次访问图片都需要建立一次tcp连接就显得低效了。因此Keep-Alive被提出用来解决效率低的问题。从HTTP/1.1起,默认都开启了Keep-Alive,保持连接特性,简单地说,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。虽然这里使用TCP连接保持了一段时间,但是这个时间是有限范围的,到了时间点依然是会关闭的,所以我们还把其看做是每次连接完成后就会关闭。后来,通过Session, Cookie等相关技术,也能保持一些用户的状态。但是还是每次都使用一个连接,依然是无状态连接。

  • 简单的http服务器
var http = require("http");
http.createServer(function(req,res){
  console.log(req.method);
  console.log(req.url);
  console.log(req.headers);
  console.log(req.httpVersion);
  res.setHeader("Content-Type","text/html");
  console.log(res.getHeader("Content-Type"));
  res.removeHeader("Content-Type");
  console.log(res.getHeader("Content-Type"));
  console.log(res.headersSent); //True if headers were sent, false otherwise.
  res.sentDate = false;
  res.statusCode = 200;
   res.write("hello");
   console.log(res.headersSent);
   res.end("world");
}).listen(8080,function(){
    console.log("server started");
});
  • 获取GET/POST数据
//判断是GET/POST请求
if(request.method == "GET"){
    //通过url模块解析路径,获取query对象
    params = url.parse(request.url,true).query;

    response.write("内容");
    response.end();
}else{
    //post请求
    var postdata = "";
    request.addListener("data",function(postchunk){
        postdata += postchunk;
    })

    request.addListener("end",function(){

        console.log(postdata); //这里获取到的就是post的数据,格式类似username=zpu&password=123,可以通过querystring来parse这个字符串,得到json对象。

        response.write("内容");
        response.end();
    })
}
  • http.request

它的功能是作为客户端向HTTP服务器发起请求。

var http = require("http");
var options = {
    hostname: "localhost",
    port: 8080,
    headers: {
        "Content-Type": "application/x-www-form-urlencoded"
    },
    path: "/",
    method: "POST"
}
var request = http.request(options, function (response) {
    response.setEncoding("utf8");
    var result = "";
    response.on("data", function (data) {
        result += data;
    });
    response.on("end", function (data) {
        console.log(result);
        console.log(JSON.parse(result));
    });
})
request.write(JSON.stringify({"name": "zpu"})); //可以简单地理解$.ajax里面的data。
request.end();

2.https

// file http-server.js
var https = require("https");
var fs = require("fs");

var options = {
  key: fs.readFileSync("./keys/server.key"),
  cert: fs.readFileSync("./keys/server.crt")
};

https.createServer(options, function(req, res) {
  res.writeHead(200);
  res.end("hello world");
}).listen(8000);

具体key和crt是如何生成的,可以参考:HTTPS证书生成原理和部署细节

通过iTerm来进行访问:

https  curl -k //localhost:8000

3.express

  • express的实现原理

初学nodejs一:别被Express的API搞晕了

var http = require("http");
var url = require("url");
var proto = {};

function createServer(){
    function app(req,res){
       app.handle(req,res);
    }
    //把proto中的属性拷贝到app对象里
    Object.assign(app,proto);
    app.stack = [];
    return app;
}
proto.use = function(route,fn){
  var path = route;
  var handle = fn;
  if(typeof route != "string"){
      handle = route;
      path = "/";
  }
  this.stack.push({handle:handle,path:path});
}

proto.handle = function(req,res){
  var stack = this.stack;
  var index = 0;
  function next(err){
      var layer = stack[index++];
      var route = layer.path;
      var handle = layer.handle;
      var path = url.parse(req.url).pathname;
      if(path.startsWith(route)){ //匹配到路径
          handle(req,res,next);
      }else{
          next();
      }
  }
  next();
}

proto.listen = function(port){
    var server = http.createServer(this);
    server.listen(port);
}

module.exports = createServer;

一个简单的中间件:

var url = require("url");
var fs = require("fs");
module.exports = function(app){
    app.use(function(req,res,next){
      res.render = function render(path, data) {
          var str = fs.readFileSync(path,"utf8");
          var tpl = str.replace(/<%=([\s\S]+?)%>/g, function (match, group) {
              return ""+obj." + group + "+"";
          });
          tpl = " var tpl = "" + tpl + ""\n return tpl;";
          var compile = new Function("obj", tpl);
          console.log(compile(data));
          this.send(compile(data));
      }
        next();
    });

    app.use(function(req,res,next){
        var urlObj = url.parse(req.url,true);
        var pathname = urlObj.pathname;
        var query = urlObj.query;
        req.path = pathname;
        req.query = query;
        next();//调用next表示可以继续调用
    });

    app.use(function(req,res,next){
        res.send =  function(html){
            res.writeHead(200,{"Content-Type":"text/html;charset=utf-8"});
            res.end(html);
        }
        next();
    });
}
  • 关于模板渲染

上面有一段代码是模板渲染:

function render(str, data) {
   // 该正则表示空白或者非空白字符,允许=号后面有空白,如传入的是<%= username %>,得到的group为“ username”
    var tpl = str.replace(/<%=([\s\S]+?)%>/g, function (match, group) {
        return ""+obj." + group + "+"";
    });
    // hello+obj.username+obj.age
    tpl = " var tpl = "" + tpl + ""\n return tpl;";
    console.log(tpl);
    var compile = new Function("obj", tpl);
    return compile(data);
}
var obj = {
    username: "zfpx",
    age: 6
}

var result = render("hello <%= username %> <%=age%>", obj);
console.log(result);

这里可以学到的东西有以下几点:

  1. 正则表达式

  2. new Function的用法 (最后一个参数为函数体里面的内容,前面是传入的参数。)

当然我们也可以用eval来实现,来并不是太好。

另外这里有一个很好玩的东西:

console

  • express用法
  1. 介绍
var express = require("express");
var app = express();
app.get("/list",function(req,res){
    res.send("list"+req.url);
});
app.post("/list",function(req,res){
    res.send("post"+req.url);
});
app.all("/all",function(req,res){
    res.send("post"+req.url);
});
app.all("*", function(req,res){
    res.send("此页面走丢了");
});
app.listen(8090);

2.中间件

a. 每个中间件都可以控制流程是否继续执行

b. req res 相同对象

c.如果出错了,转交错误处理中间件进行处理

app.use(function(req,res,next){
    next(“出错了")
});
app.use(function(err,req,res){
    console.error(err);
    res.end(err.message);
});

3.参数

req.query:用来处理URL中?的参数,如localhost:8090/?a=2&b=2,得到的req.query就是{a: 2, b: 2}
req.params: 一般对应restful api的URL,譬如我们的路由URL是“/article/:id/:name”,当写成真实地址后,req.param得到的是{id: ”你输入的", name: "你输入的"}
req.body: 请求体,可以理解成post的数据
req.headers:请求头

4.发送

res.send("内容")
res.sendStatus(400);
res.render(); //渲染

5.视图

// 设置view目录
app.set("views", __dirname + "/views");

// 设置模板引擎
app.set("view engine", "jade");

这里将模板设置为jade,当然你也可以设置成react之类的

6.静态文件

express.static(root, [options])

其中options中有一个dotfiles,表示点文件,譬如.gitignore类似这样的,测试了一下MS选择ignore(忽略)或者deny(拒绝)没什么区别。

具体参考API

7.bodyPaser

之前有提过,可以用它来解析post的数据

app.use(bodyParser.json());
//querystring  false
app.use(bodyParser.urlencoded({extended:true}));

bodyParser中间件的研究

本文链接:www.my-fe.pub/post/node-study-part3.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。