使用Node进行网络开发

用户在浏览器中输入网址 --> 获得网页的过程经历了几个步骤:

  1. 通过浏览器发送一个请求到服务器(期间经历的DNS解析、TCP3次握手连接)

  2. 服务器分析、处理用户的请求,并生产请求的内容,然后发送给浏览器

  3. 浏览器解析服务发送回的数据,生产网页

服务器

在HTTP协议中的服务器指:监听客户端的请求,并且根据请求的内容进行相应处理,返回响应给客户端。

Node中可以利用http模块进行Web服务器的搭建。

http模块

  1. http模块不是Node的核心模块,需要利用require()加载

  2. 使用http.createServer()方法创建一个http服务器,返回一个http服务器对象

    • 利用服务器对象进行开发

    • 接收的回调函数是监听到客户端请求时的回调函数

  3. server.listen()方法可以开启Web服务,监听某台主机的某个端口

    • 参数是(port, hostname, backlog, callback)

    • port是监听主机的端口号;hostname是监听主机的IP地址;callback是服务器成功开启后的回调函数(开启服务器后,再连接数据库

      // 搭建一个HTTP服务器,用于处理用户发送的http请求
      // http模块不是核心模块,需要require()导入
      var http = require('http');
      
      // http模块的createServer()方法可以返回一个标准http服务器对象
      // 通过服务器对象来进行开发
      // callback是监听到客户端连接的回调函数
      var server = http.createServer(callback);
      
      // 服务器监听某个网卡上的某个端口,开启服务
      // listen(port, hostname, backlog, callback)
      // port是监听的端口,hostname是监听主机的ip地址,callback是服务器开启成功后的回调函数
      
      // 服务器开启失败时触发error事件
      server.on('error', function (err) {
        console.log(err);
      })
      
      // listening事件在服务器开启成功时触发
      server.on('listening', function () {
        console.log('listening...8080');
      })
      
      // 监听来自客户端的请求事件,在接收到请求时触发
      server.on('request', function () {
        console.log("there is a request");
      })
      
      server.listen(8080, 'localhost');

主要的事件

  1. 服务器开启失败的事件error的监听:server.on('errro', function(err) {})

  2. 服务器开启成功的事件listening的监听:server.on('listening', function(err) {})可以写在server.listen()方法的回调函数中

  3. 服务器接收到请求的事件request的监听:server.on('request', function(err, req, res) {})可以写在http.createServer()方法的回调函数中

参数对象

request事件接收两个参数:

  • request对象:提供客户端请求相关数据的对象,是http.IncomingMessage类的一个实例,有对应的属性与方法

    • httpVersion属性:使用的HTTP协议版本

    • header属性:请求头中相关的数据

    • url属性:

    • method属性:请求的方式

    • ...

  • response对象:服务端向客户端发送的响应数据对象,是http.ServerResponse类的一个实例

    • write(chunk, [encoding]):发送一个数据库到响应的正文(网页的内容数据)中,需要在发送完成后调用res.end()方法

    • end(chunk, [encoding], [callback]):在通过write()方法发送完所有正文和头信息后,需要res.end()告诉服务器数据全部发送完成。每个响应都必须调用res.end()方法,并且在最后调用
      如果指定了 chunk,则它等同于调用 response.write(chunk, encoding) 之后调用 response.end(callback)

    • writeHeader(statusCode [, statusMessage] [, headers]):写入头信息,res.write()前调用,并且一次响应中只能调用一次,头信息写在一个对象中

    • statusCode属性和setHeader()方法可以组合实现writeHeader()的功能

      server.on('request', function (req, res) {
      console.log("there is a request");
      var chunk = '<h1>hello world</h1>';
      
      // Buffer.byteLength(chunk)以字节为单位,告诉浏览器以纯文本解析传回的数据
      res.writeHeader(200, 'OK', {'Content-type': 'text/plain', 'Content-length': Buffer.byteLength(chunk)});
      
      res.end(chunk, 'utf-8');
      })

url处理

根据用户请求的path返回不同的数据,使用req.url可以获取path信息。

  • req.url?后面的部分叫做query

  • Node下的url模块可以处理请求的中的req.url

  • 使用switch结构,为不同的pathname划分不同的逻辑处理方式

    server.on('request', function (req, res) {
    console.log(req.url);   //  req.url获取请求的路径path信息
    var urlStr = url.parse(req.url);
    switch(urlStr.pathname) {
      case '/':  // 首页
    res.writeHeader(200, 'OK', {'Content-type': 'text/plain'});
    res.end('<h1>首页</h1>', 'utf-8');
    break;
    
      case '/users':  // 用户页
    res.writeHeader(200, 'OK', {'Content-type': 'text/plain'});
    res.end('<h1>用户页</h1>', 'utf-8');
    break;
    
      default :  // 不存在
    res.writeHeader(404, 'Not Found', {'Content-type': 'text/plain'});
    
    res.end('<h1>出错</h1>', 'utf-8');
    break;
    }
    })

使用fs模块实现行为与表现分离

根据用户不同访问的不同路径,执行不同操作,读取不同页面

server.on('request', function (req, res) {
  console.log(req.url);
  var urlStr = url.parse(req.url);

  switch(urlStr.pathname) {
    case '/':  // 首页
      sendData(htmlDir + '/index.html', req, res);  // 请求不同的路径,读取不同的页面返回给客户端
      break;

    case '/users':  // 用户页
      sendData(htmlDir + '/user.html', req, res);
      break;

    default :  // 不存在
      sendData(htmlDir + '/404.html', req, res);
      break;
  }
});

//  每次请求都读取静态页面,再输出。   fs.readFile()方法封装了fs.open()、fs.read()和fs.close()方法
// fs.readFile()返回值是原始的buffer对象
function sendData(file, req, res) {
  fs.readFile(file, function (err, data) {
    if(err) {
      res.writeHeader(404, "Not Found", {'Content-Type': 'text/html'});
      res.end('Not Found');
    } else {
      res.writeHeader(200, "OK", {'Content-Type': 'text/html'});
      res.end(data);
    }
  });
}

处理GETPOST请求提交的数据

  • 通过GET方法传递的数据可以使用url.parse(req.url).query获取到GET请求提交的数据,使用querystring内置模块(无需加载)来解析

  • GET请求提交的数据绑定在url中,使用querystring.parse()方法解析urlquery属性便可以得到GET请求提交的数据

  • POST请求提交的数据在HTTP的Body中,服务器接收到的是数据流(因为POST请求提交的数据量较大,需要从缓冲区区读取chunk

    • 利用request对象的data事件,触发回调函数接收所有提交的数据。可以将其拼接到一个字符串上

    • 接收完所有数据后触发end事件,此时才可以使用querystring.parse()解析接收到的所有数据

      var http = require('http');
      var url = require('url');
      var fs = require('fs');
      var querystring = require('querystring');
      
      var server = http.createServer();
      
      var htmlDir = __dirname + '/html/';   // 使用fs读取html目录下的文件
      
      server.on('request', function (req, res) {
        var urlStr = url.parse(req.url);
        switch(urlStr.pathname) {
          case '/':  // 首页
            sendData(htmlDir + '/index.html', req, res);
            break;
      
          case '/users':  // 用户页
            sendData(htmlDir + '/user.html', req, res);
            break;
      
          // 增加登录页面的路由
          case '/login':  // 用户页
            sendData(htmlDir + '/login.html', req, res);
            break;
          // 增加登录页面提交数据的处理
          case '/login/check':  // 用户页
            if(req.method.toUpperCase() === 'GET') {
              console.log(querystring.parse(urlStr.query));    // 使用querystring.parse()将传递的数据解析为一个对象
            }
            if(req.method.toUpperCase() === 'POST') {
              var str = '';   // 用来接收POST传递来的数据
              req.on('data', function (chunk) {
                str += chunk;
              })
              req.on('end', function () {
                console.log(querystring.parse(str));
              })
            }
            break;
      
          default :  // 不存在
            sendData(htmlDir + '/404.html', req, res);
            break;
        }
      });
      
      //  每次请求都读取静态页面,再输出。   fs.readFile()方法封装了fs.open()、fs.read()和fs.close()方法
      function sendData(file, req, res) {
        fs.readFile(file, function (err, data) {
          if(err) {
            res.writeHeader(404, "Not Found", {'Content-Type': 'text/html'});
            res.end('Not Found');
          } else {
            res.writeHeader(200, "OK", {'Content-Type': 'text/html'});
            res.end(data);
          }
        });
      }
      
      server.listen(8080, 'localhost');

常识

  • http模块的服务器默认返回的数据类型是text/html

  • http模块中的http.STATUS_CODES属性包含所有的状态码及其描述信息


Kyxy
316 声望10 粉丝