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);
  • 相关阅读:
    Elementary Methods in Number Theory Exercise 1.2.25
    Elementary Methods in Number Theory Exercise 1.2.14
    图解欧几里德算法
    图解欧几里德算法
    Elementary Methods in Number Theory Exercise 1.2.14
    Android中的长度单位详解(dp、sp、px、in、pt、mm)
    分享下多年积累的对JAVA程序员成长之路的总结
    android异常之都是deamon惹的祸The connection to adb is down, and a severe error has occured.
    TomatoCartv1.1.8.2部署时报错
    JavaScript浏览器对象之二Document对象
  • 原文地址:https://www.cnblogs.com/lyraLee/p/12264386.html
Copyright © 2011-2022 走看看