资讯专栏INFORMATION COLUMN

express基本原理

endiat / 468人阅读

摘要:了解原理之前,你需要先掌握的基本用法。基本结构先回顾一下使用的的过程,首先是把模块倒入,然后当做方法执行,在返回值中调用处理路由,调用监听端口。大多数中间件也是这个原理,如模块,给它加个属性即可。

了解 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查看代码请点击:传送门

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105140.html

相关文章

  • Session原理、安全以及最基本Express和Redis实现

    摘要:基于以上原因,很多管理都是基于实现的。在经过中间件的时候就会自动完成的有效性验证延期重新颁发以及对中数据的获取了。上述代码只是对于请求的静态处理,整个用户管理的另一个方面则是状态的切换用户的登陆登出以及用户数据的获取。 基础概念 Session管理是Web Application的基础也是一个老生常谈的话题。为了方便后文的展开,更重要的是确认自己清晰的理解了整个Session管理的概...

    Airmusic 评论0 收藏0
  • 九种 “姿势” 让你彻底解决跨域问题

    摘要:什么是跨域当协议域名端口号,有一个或多个不同时,有希望可以访问并获取数据的现象称为跨域访问,同源策略限制下都是不支持跨域的。命名是随意的,只要是符合一级域名与二级域名的关系即可,然后访问。 showImg(https://segmentfault.com/img/remote/1460000018998493); 阅读原文 同源策略 同源策略/SOP(Same origin pol...

    charles_paul 评论0 收藏0
  • 一步一步搭建一个图片上传网站(后台服务器用nodejs)

    摘要:把文件上传路径指定到然后用当前日期和文件名命名上传过来的文件。后端利用建立服务器,利用中间件指定文件路径。利用这个前端和后端技术,我们基本上就可以做出一个图片上传存储的基本网站核心。 前几天看了腾讯云社区的一个文件上传的文章文件上传那些事儿,大体上讲了以下h5中图片上传的几个核心原理,但是没有后端接受的服务器代码,没法做测试。也没有具体的一个实例都是一些基本的原理片段,并且ui界面也不...

    Darkgel 评论0 收藏0
  • 干货实例:什么是React服务端渲染?

    摘要:今天分享一篇公司大佬的文章,非常厉害的大神崇拜脸,讲讲服务端渲染。服务端渲染,它到底用了什么原理呢服务端渲染原理服务端渲染的方式有很多,主流的服务端语言为使用渲染。 富婆来报道,今天想问题想不出来,随手抓了一下头发,没想到啊没想到,我那浓(mei)密(sheng)茂(ji)盛(gen)的秀发又少了好几根,一定要改掉这个想不出来问题就揪头发的坏习惯。今天分享一篇公司大佬的文章,非常厉害的...

    Jason_Geng 评论0 收藏0

发表评论

0条评论

endiat

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<