zoukankan      html  css  js  c++  java
  • express基础

    express.js是nodejs的框架。它将http-server进行了封装。

    1. express基础源码

    let http = require('http');
    let methods = require('methods');//express依赖的第三方库,包含所有的方法
    let url = require('url');
    let fs = require('fs');
    let path = require('path');
    
    function application() {
      function app(req, res) {// http.createServer(function(){})中的监听函数
        let { pathname } = url.parse(req.url);
        let reqMethod = req.method.toLowerCase();
        let index = 0;
        function next(err) {//如果next传参,表示抛出异常
          // 递归终止条件;即遍历完app.routes仍然调用了next方法
          if (index === app.routes.length) {
            res.statusCode = 404;
            // 任何路由都不匹配
            return res.end(`Cannot ${reqMethod.toUpperCase()} ${pathname}`);
          };
    
          const layer = app.routes[index++];
          const { path, method, handler } = layer;
          if (err) {
            // 错误捕获中间件
            if (method === 'middleware' && handler.length === 4) {
              return handler(err, req, res)
            } else {
              next();
            }
          } else {
            if (method === 'middleware') {// 中间件 
              // 中间件的路由匹配
              if(path === pathname || path === '/' || path.startsWith(path+'/')) {
                return handler(req,res,next);
              } else {//如果不能匹配中间件的路由
                next();
              }
            } else {
              if(path.params) {// 带参数的路径
                let matches = pathname.match(path);// 取出路径中对应的参数
                if (matches && method === reqMethod) {
                  let [, ...args] = matches;
                  req.params = path.params.reduce((memo, curr, index) => (memo[curr] = args[index],memo), {});
                  return handler(req, res);          
                }
              }
              if((pathname === path || pathname === '*') && 
                (method === reqMethod || method === 'all')) {
                return handler(req,res);
              }  
              next();      
            }  
          }
        }
        next();
      }
      app.routes = [];
      [...methods, 'all'].forEach(item => {
        app[item] = function(path, handler) {
          if(path.includes(':')) {// 路径含有路径参数
            let params = [];
            let str = path.replace(/:([^/]*)/g, function() {
              params.push(arguments[1]);
              return '([^/]*)';// 用于匹配用户输入的带参数的路由
            })
            path = new RegExp(str);
            path.params = params;
          }
          const layer = {
            method: item,
            path,
            handler
          }
          app.routes.push(layer);
        }
      })
      // app中使用中间件;
      app.use = function(path, handler) {
        if(typeof handler !== 'function') {// 如果有路由
          handler = path;
          path = '/';
        }
        const layer = {
          path, 
          method: 'middleware',
          handler
        }
        app.routes.push(layer);
      } 
      //内置中间件执行;添加公共属性和方法
      app.use(function(req, res, next) {
        const { query, path } = url.parse(req.url, true);
        req.query = query;
        req.path = path;
        res.send = function(value) {
          if(typeof value === 'object') {//json对象
            res.setHeader('Content-Type', 'application/json;charset=utf-8');
            res.end(JSON.stringify(value));
          } else if(typeof value === 'number') {//校验码
            const status = require('_http_servet').status_CODES;
            res.end(status[value]);
          } else if(typeof value === 'string' || Buffer.isBuffer(value)) {
            res.setHeader('Content-Type', 'text/html;charset=utf-8');
            res.end(value);
          }
        }
        res.sendFile = function(pathname) {
          return fs.createReadStream(pathname).pipe(res);
        }
        next();
      })
      app.listen = function(port) {
        let server = http.createServer(app);
        server.listen(port);
      }
      return app;
    }
    // 高阶函数
    application.static = function(root) {
      return function(req, res, next) {
        let absPath = path.join(root, req.url);
        fs.stat(absPath, function(err, statObj) {
          if(err) {
            return next();
          }
          if (statObj.isDirectory()) {
            absPath = absPath + 'index.html';
          }
          fs.createReadStream(absPath).pipe(res);
        })
      }
    }
    module.exports = application;

    2. express中间件

    请求到路径处理之前,在中间执行的内容,就是中间件。
    中间件的应用:
    1. 可以在中间件中添加权限校验
    2. 可以在中间件中添加公共属性和方法
    3. 中间件的回调函数中有三个参数(req, res, next), 其中用户可以通过next方法决定是否继续执行

     中间件分为三类:

    1. 自定义中间件

    app.use(path, function(req,res,next) {
        // 向下执行
        next();
    })

    2. 特殊中间件

    1. 静态服务中间件

    静态服务中间件用于服务端提供静态资源服务。

    const app = express();
    
    const root = path.join(__dirname, './index.html')
    // 如果root=__dirname表示提供基于根目录的静态服务
    app.use(express.static(root));

    2. 内置中间件

    内置中间件主要提供了公共属性(req.query, req.path)和公共方法res.send, res.sendFile。

    1)公共属性

        const { query, path } = url.parse(req.url, true);
        req.query = query;
        req.path = path;

    2)公共方法

       // send方法根据传递的参数类型不同,返回不同的结果
       res.send = function(value) {
          if(typeof value === 'object') {//json对象
            res.setHeader('Content-Type', 'application/json;charset=utf-8');
            res.end(JSON.stringify(value));
          } else if(typeof value === 'number') {//校验码
            const status = require('_http_servet').status_CODES;
            res.end(status[value]);
          } else if(typeof value === 'string' || Buffer.isBuffer(value)) {
            res.setHeader('Content-Type', 'text/html;charset=utf-8');
            res.end(value);
          }
        }
        // sendFile接受一个路径参数,返回路径对应的文件内容
        res.sendFile = function(pathname) {
          return fs.createReadStream(pathname).pipe(res);
        }

    3. 错误捕获中间件

    next方法传递的参数即抛出的错误信息。通过错误捕获中间件进行捕获。

    错误捕获中间件的特点是,回调函数含有4个参数,第一个参数是获取到的error信息。

    app.use('/user/', function(req, res, next) {
      const absPath = path.resolve(__dirname, './index.html');
      res.sendFile(absPath);
      next('dddd'); //抛出错误信息;
    });
    // 由于上面抛出错误,该步不执行
    app.get('/read', function(req, res) {
      res.send(req.session); 
    });
    // 错误捕获中间件;
    app.use(function(err, req, res, next) {
      console.log(err); // dddd
    });

    3. 第三方中间件

    1. body-parser

    根据请求头的Content-Type获取请求体的数据类型,然后将其解析后的数据赋值到req.body中。

    app.use(bodyParser.json());// application/json
    // extended=true,表示使用qs库;=false表示使用querystring库
    app.use(bodyParser.urlencoded({extended: true}));// application/x-www-form-urlencoded

    2. cookie-parser

    1) 接受一个参数,该参数是签名cookie的密钥。并且可以通过req.secret获取该密钥。

      参数存在时express中的API中res.cookie(key, value, options)中options中设置signed: true生效,

      写入客户端的cookie是签名后的cookie。

    2)解析cookie, 通过req.cookies可以获取所有的非签名的cookie。

        req.signedCookies获取到的是签名的cookie

    app.use(cookieParser('lyra'));

    3. express-session

    1)向客户端写入sessionId对应的cookie, session的数据存在服务器端。

    可以通过服务器端设置req.session[key] = value进行数据添加和读取。

    2)接受一个对象,用于设置参数。

    其中secret对应的参数,如果使用可cookie-parser,则需要保持两者一致。

    app.use(session({ // 会向客户端发送一个key为connect.id, value为唯一标识的cookie。
      resave: true,
      saveUninitialized: true,
      cookie: {httpOnly: true}, //设置connect.id对应的cookie的属性
      secret: 'lyra' //加密和cookieParser的密钥一致
    }));

    3. 示例

    let express = require('express');
    let path = require('path');
    let bodyParser = require('body-parser');
    let cookieParser = require('cookie-parser');
    let session = require('express-session');
    
    
    let app = express(); // app本质是监听函数
    /**
     * 自定义中间件: 
     * 1. 可以在中间件中进行权限校验
     * 2. 可以在中间件中添加公共方法和属性
     * 3. 中间件的回调函数有next方法,可以由用户调用与否,决定是否向下执行
     * 特殊中间件:
     * 1. 错误捕获中间件: next方法传递的参数视为抛出的错误信息
     * 2. 内置中间件: 添加公共属性和方法(path, query, send, sendFile)
     * 3. 静态服务中间件: 启动静态服务(高阶函数)
     * 第三方中间件:
     * 1. body-parser: 解析请求体req.body(json/x-www-form-urlencoded)
     * 2. cookie-parser: 1)解析cookie, 赋值req.cookies返回未签名cookie 
     *                     req.signedCookies返回签过名的cookie;示例: {name1: lyra}
     *                   2)支持cookie签名,使得express的res.cookie的signed参数生效
     *                     cookieParser传递的参数是密钥,通过req.secret获取
     * 3. express-session: session数据存储在服务端,客户端存储的是sessionId
     *                     1)在req上提供一个session对象;session 
     *                     2)提供一个密钥
     */
    // 静态服务中间件
    app.use(express.static(__dirname));
    app.use(function(req, res, next) {
      next();
    });
    app.use(bodyParser.json());// application/json
    // extended=true,表示使用qs库;=false表示使用querystring库
    app.use(bodyParser.urlencoded({extended: true}));// application/x-www-form-urlencoded
    
    app.use(cookieParser('lyra'));
    
    app.use(session({ // 会向客户端发送一个key为connect.id, value为唯一标识的cookie。
      resave: true,
      saveUninitialized: true,
      secret: 'lyra' //加密和cookieParser的密钥一致
    }));
    app.use('/user/', function(req, res, next) {
      const absPath = path.resolve(__dirname, './index.html');
      res.sendFile(absPath);
      next('dddd');
    });
    app.get('/read', function(req, res) {
      res.send(req.session);
    });
    app.get('/write', function(req, res) {
      // 手动设置session
      req.session.a = 'hello';
      //express设置cookie;时间为ms
      res.cookie('name', 'lyra', {maxAge: 20000, signed: false});
      res.cookie('name1', 'lyra', {maxAge: 20000, signed: true});
      res.end();
    });
    app.get('/user/:name/:id', function(req, res) {
      // console.log(req.params);
    });
    app.post('/post', function(req, res) {
      res.send(req.body);
    });
    app.use(function(err, req, res, next) {
      console.log(err);
    });
    
    
    app.listen(3000);
  • 相关阅读:
    Jmeter中的几个重要测试指标释义
    接口测试考虑
    树Hash学习笔记
    kali linux主题美化
    Kali Linux 终端字体配色
    解决github无法打开
    Python requests数据的content和text的区别
    Python+adb操作移动端自动化测试
    linux后台运行python程序
    洛谷P2791 幼儿园篮球题 另解
  • 原文地址:https://www.cnblogs.com/lyraLee/p/12264386.html
Copyright © 2011-2022 走看看