express基本原理

栏目: Node.js · 发布时间: 7年前

内容简介:了解 express 原理之前,你需要先掌握 express 的基本用法。关于 express 的介绍请看先回顾一下 express 使用的的过程,首先是把模块倒入,然后当做方法执行,在返回值中调用

了解 express 原理之前,你需要先掌握 express 的基本用法。

关于 express 的介绍请看 express 官网

基本结构

先回顾一下 express 使用的的过程,首先是把模块倒入,然后当做方法执行,在返回值中调用 use 处理路由,调用 listen 监听端口。

const express = require('express')
const app = express()
app.use('/home', (req, res) => {
  res.end('home')
})
app.listen(8080, () => {
  console.log('port created successfully')
})

根据上面的使用,我们开始构建代码。我们需要写一个 express 方法,返回一个 app 对象,有 uselisten 方法。

const http = require('http')
const url = require('url')

function express() {
  const app = {}
  const routes = [];
  
  app.use = function (path, action) {
    routes.push([path, action])
  }

  function handle(req, res) {
    let pathname = url.parse(req.url).pathname;
    for (let i = 0; i < routes.length; i++) {
      var route = routes[i];
      if (pathname === route[0]) {
        let action = route[1];
        action(req, res);
        return;
      }
    }
    handle404(req, res);
  }

  function handle404(req, res) {
    res.end('404')
  }

  app.listen = function (...args) {
    const server = http.createServer((req, res) => {
      handle(req, res)
    })
    server.listen(...args)
  }

  return app
}

module.exports = express

上面代码中的 use 方法的作用是把请求路径跟对应的处理函数存放在一个数组中,当请求到来的时候遍历数组,根据路径找到对应的方法执行。

动态路由

动态路由是根据参数可以动态匹配路径。

const express = require('./express')
const app = express()

// /home/1
// /home/2
app.use('/home/:id', (req, res) => {
  res.end('home')
})

app.listen(8080, () => {
  console.log('port created successfully')
})

根据路由里面的参数要匹配符合规则的路由我们需要使用正则来处理,下面代码是根据路径来生成正则的一个方法。

const pathRegexp = (path, paramNames=[], {end=false} ={}) => {
  path = path
    .concat(end ? '' : '/?')
    .replace(/\/\(/g, '(?:/')
    .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?(\*)?/g, function (_, slash, format, key, capture, optional, star) {
      slash = slash || '';
      paramNames.push(key);
      return ''
        + (optional ? '' : slash)
        + '(?:'
        + (optional ? slash : '')
        + (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
        + (optional || '')
        + (star ? '(/*)?' : '');
    })
    .replace(/([\/.])/g, '\\$1')
    .replace(/\*/g, '(.*)');
  return new RegExp('^' + path + '$')
}

module.exports = pathRegexp

根据路径生成正则也是有第三方模块 path-to-regexp 模块,核心原理大家值得参考。包括 Vue 和 React 的路由都使用到了这个模块。

下面我们需要开始动态映射路由。

const http = require('http')
const url = require('url')
const pathRegexp = require('./pathRegexp')

function express() {
  const app = {}
  const routes = { 'all': [] };

  app.use = function (path, action) {
    const keys = []
    const regexp = pathRegexp(path, keys,{end:true})
    routes.all.push([
      { regexp, keys },
      action
    ]);
  };

  ['get', 'put', 'delete', 'post'].forEach(function (method) {
    routes[method] = [];
    app[method] = function (path, action) {
      const keys = []
      const regexp = pathRegexp(path, keys, {end:true})
      routes[method].push([
        { regexp, keys },
        action
      ]);
    };
  });

  const match = function (pathname, routes, req, res) {
    for (var i = 0; i < routes.length; i++) {
      let route = routes[i];
      let reg = route[0].regexp;
      let keys = route[0].keys;
      let matched = reg.exec(pathname);
      if (matched) {
        let params = {};
        for (let i = 0, l = keys.length; i < l; i++) {
          let value = matched[i + 1];
          if (value) {
            params[keys[i]] = value;
          }
        }
        req.params = params;
        let action = route[1];
        action(req, res);
        return true;
      }
    }
    return false;
  };

  function handle(req, res) {
    let {pathname, query} = url.parse(req.url, true);
    req.query = query
    let method = req.method.toLowerCase();
    if (routes.hasOwnProperty(method)) {
      if (match(pathname, routes[method], req, res)) {
        return;
      } else {
        if (match(pathname, routes.all, req, res)) {
          return;
        }
      }
    } else {
      if (match(pathname, routes.all, req, res)) {
        return;
      }
    }
    handle404(req, res);
  }

  function handle404 (req, res) {
    res.end('404')
  }

  app.listen = function (...args) {
    const server = http.createServer((req, res) => {
      handle(req, res)
    })
    server.listen(...args)
  }
  return app
}

module.exports = express

其中 express 会把请求的方法都代理到 app 中作为属性的方式来方便用户使用。

const express = require('./express')
const app = express()

app.use('/home/:id', (req, res) => {
  console.log(req.params)
  res.end('home')
})

// app.get 
// app.post 
app.get('/user', (req, res) => {
  console.log(req.query)
  res.end('user')
})

app.listen(8080, () => {
  console.log('port created successfully')
})

express 会在请求对象中加一些属性,会把路径参数作为请求时的 params 属性,会把查询字符串作为请求时的 query 属性。大多数中间件也是这个原理,如 body-parser 模块,给它加个 body 属性即可。

通过GitHub查看代码请点击:

express基本原理


以上所述就是小编给大家介绍的《express基本原理》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Google's PageRank and Beyond

Google's PageRank and Beyond

Amy N. Langville、Carl D. Meyer / Princeton University Press / 2006-7-23 / USD 57.50

Why doesn't your home page appear on the first page of search results, even when you query your own name? How do other web pages always appear at the top? What creates these powerful rankings? And how......一起来看看 《Google's PageRank and Beyond》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

随机密码生成器
随机密码生成器

多种字符组合密码