欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript 實(shí)現(xiàn)類似Express的中間件系統(tǒng)(實(shí)例詳解)

 更新時(shí)間:2023年02月14日 15:44:11   作者:鋒利的綿羊  
在 Express 中可以給一個(gè)請(qǐng)求設(shè)置若干個(gè)中間件,在處理響應(yīng)時(shí)會(huì)按順序執(zhí)行這些中間件,正在執(zhí)行的中間件可以控制是否執(zhí)行下一個(gè)中間件,這篇文章主要介紹了JavaScript 實(shí)現(xiàn)類似Express的中間件系統(tǒng)的相關(guān)資料,需要的朋友可以參考下

Express 的中間件系統(tǒng)

在 Express 中可以給一個(gè)請(qǐng)求設(shè)置若干個(gè)中間件,在處理響應(yīng)時(shí)會(huì)按順序執(zhí)行這些中間件,正在執(zhí)行的中間件可以控制是否執(zhí)行下一個(gè)中間件。

模擬實(shí)現(xiàn)的 Express 將擁有這些功能:

  • Express 類擁有三個(gè)實(shí)例方法: run(url) 開(kāi)始執(zhí)行中間件,接收 url。
  • use(fn) 設(shè)置應(yīng)用中間件,在路由中間件之前執(zhí)行。
  • get(url, fn) 設(shè)置路由中間件,只在 url 與請(qǐng)求路由一致時(shí)執(zhí)行。
  • fn 的定義為:(req:any, res:any, next) => void
  • fn 中調(diào)用 next() 方法執(zhí)行下一個(gè)中間件。
  • fn 中調(diào)用 res.end(response) 方法后將本次響應(yīng)值為 response 并且不再執(zhí)行后續(xù)中間件。
  • req 和 res 會(huì)逐層傳遞,可以被修改。
  • 調(diào)用 run(url) 方法后開(kāi)始執(zhí)行中間件。
  • 如果沒(méi)有注冊(cè)對(duì)應(yīng)的路由中間件,則只執(zhí)行應(yīng)用中間件。
  • 如果有對(duì)應(yīng)的路由中間件,則先執(zhí)行應(yīng)用中間件后執(zhí)行路由中間件。
  • 中間件按照注冊(cè)順序執(zhí)行。

大概的使用方式如下:

class Express {}

const app = new Express();

app.use(function (req, res, next) {
  console.log("mid");
  next();
});

app.use(function (req, res, next) {
  console.log("mid");
  next();
});

app.get("/home", function (req, res, next) {
  console.log("page home");
  next();
});

app.get("/detail", function (req, res, next) {
  console.log("page detail");
  next();
});

app.run("/home");

實(shí)現(xiàn)代碼

const appMiddlewareKey = Symbol("app_middleware");
class Express {
  constructor() {
    this.middleware = {};
  }

  use(fn) {
    this.get(appMiddlewareKey, fn);
  }

  get(url, fn) {
    if (!this.middleware[url]) {
      this.middleware[url] = [];
    }

    function wrap(ctx) {
      return new Promise((resolve, reject) => {
        ctx.res.end = (response) => {
          reject(response);
        };
        try {
          const result = fn(ctx.req, ctx.res, () => {
            resolve(ctx);
          });
          if (result instanceof Promise) {
            result.catch(reject);
          }
        } catch (error) {
          reject(error);
        }
      });
    }

    this.middleware[url].push(wrap);
  }

  run(url) {
    const chain = [].concat(this.middleware[appMiddlewareKey]);
    const route = this.middleware[url];
    if (route) {
      chain.push(...route);
    }
    const ctx = {
      req: {
        url,
      },
      res: {},
    };

    let promise = Promise.resolve(ctx);
    chain.forEach((middleware) => {
      promise = promise.then(middleware);
    });
    return promise.then((ctx) => ctx.res);
  }
}

如何實(shí)現(xiàn)異步執(zhí)行鏈

在調(diào)用 use() 方法或 get() 方法設(shè)置中間件時(shí),將傳入的回調(diào)函數(shù)包裝成一個(gè)返回 Promise 對(duì)象的新函數(shù)。

function wrap(ctx) {
  return new Promise((resolve, reject) => {
    // 提供停止執(zhí)行中間件的方法
    ctx.res.end = (response) => {
      reject(response);
    };
    try {
      const result = fn(ctx.req, ctx.res, () => {
        resolve(ctx);
      });
      if (result instanceof Promise) {
        result.catch(reject); // 如果傳入中間件回調(diào)返回值為 Promise
      }
    } catch (error) {
      reject(error);
    }
  });
}
this.middleware[url].push(wrap);

在調(diào)用 .run() 方法時(shí),先根據(jù)傳入的 url 決定要執(zhí)行的中間件,然后遍歷中間件列表,拼接成一個(gè) Promise 鏈。

let promise = Promise.resolve(ctx);
chain.forEach((middleware) => {
  promise = promise.then(middleware);
});

如何將控制權(quán)交給中間件函數(shù)

wrap() 方法中,給實(shí)際的中間件函數(shù)傳遞一個(gè)方法,這個(gè)方法調(diào)用后 wrap() 返回的 Promise 才會(huì)被 resolve,這個(gè)方法就是 next() 方法。

const result = fn(ctx.req, ctx.res, () => {
  resolve(ctx);
});

使用示例

應(yīng)用級(jí)中間件與路由級(jí)中間件

use() 綁定的中間件為應(yīng)用級(jí)中間件,用 get() 綁定的為路由級(jí)中間件。應(yīng)用級(jí)中間件總是會(huì)執(zhí)行,只調(diào)用 .run() 方法接收到的對(duì)應(yīng) url 的路由中間件。

以下例子不調(diào)用 /detail 的路由中間件。

app.use(function (req, res, next) {
  res.name = "zhangkb";
  next();
});

app.get("/home", function (req, res, next) {
  console.log("page home", req);
  next();
});

app.get("/detail", function (req, res, next) {
  console.log("page home", req);
  next();
});

app.run("/home");

.run() 的返回值與異常處理

.run() 方法返回一個(gè) Promise 對(duì)象,如果所有中間件都執(zhí)行完畢無(wú)異常則返回 res 對(duì)象:

app.use(function (req, res, next) {
  res.name = "zhangkb";
  next();
});

app.run("/").then((res) => {
  console.log("success", res);
});

如果在中間件中用 throw 關(guān)鍵字拋出異?;蛘哒{(diào)用 res.end() 方法,則會(huì)停止執(zhí)行后續(xù)中間件,并將拋出的異常對(duì)象(或 res.end() 的參數(shù))作為 Promise 的失敗原因。

app.use(function (req, res, next) {
  res.name = "zhangkb";
  res.end(new Error("reason")); // 主動(dòng)停止
  throw new Error("reason"); // 拋出異常
  next();
});

app.run("/").catch((error) => {
  console.log("error", error);
});

到此這篇關(guān)于JavaScript 實(shí)現(xiàn)類似Express的中間件系統(tǒng)的文章就介紹到這了,更多相關(guān)js Express的中間件系統(tǒng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • js學(xué)習(xí)總結(jié)_輪播圖之漸隱漸現(xiàn)版(實(shí)例講解)

    js學(xué)習(xí)總結(jié)_輪播圖之漸隱漸現(xiàn)版(實(shí)例講解)

    下面小編就為大家?guī)?lái)一篇js學(xué)習(xí)總結(jié)_輪播圖之漸隱漸現(xiàn)版(實(shí)例講解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-07-07
  • 快速對(duì)接payjq的個(gè)人微信支付接口過(guò)程解析

    快速對(duì)接payjq的個(gè)人微信支付接口過(guò)程解析

    這篇文章主要介紹了快速對(duì)接payjq的個(gè)人微信支付接口過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • JavaScript中this詳解

    JavaScript中this詳解

    都說(shuō) JavaScript 是一種很靈活的語(yǔ)言,這其實(shí)也可以說(shuō)它是一個(gè)混亂的語(yǔ)言。它把函數(shù)式編程和面向?qū)ο缶幊挑酆弦黄?,再加上?dòng)態(tài)語(yǔ)言特性,簡(jiǎn)直強(qiáng)大無(wú)比,下面小編給大家介紹Javascript中this詳解,需要的小伙伴可以來(lái)參考下
    2015-09-09
  • 老生常談js中0到底是 true 還是 false

    老生常談js中0到底是 true 還是 false

    下面小編就為大家?guī)?lái)一篇老生常談js中0到底是 true 還是 false。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-03-03
  • 完美的js圖片輪換效果

    完美的js圖片輪換效果

    這篇文章主要為大家詳細(xì)介紹了完美的js圖片輪換效果,包括左右移動(dòng)和緩動(dòng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • 理解JavaScript中的事件

    理解JavaScript中的事件

    理解JavaScript中的事件...
    2006-09-09
  • ES6新數(shù)據(jù)結(jié)構(gòu)Set與WeakSet用法分析

    ES6新數(shù)據(jù)結(jié)構(gòu)Set與WeakSet用法分析

    這篇文章主要介紹了ES6新數(shù)據(jù)結(jié)構(gòu)Set與WeakSet用法,結(jié)合實(shí)例形式簡(jiǎn)單分析了Set與WeakSet的功能、使用方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2017-03-03
  • Echarts圖例組件的屬性與源代碼

    Echarts圖例組件的屬性與源代碼

    圖例組件展現(xiàn)了不同系列的標(biāo)記(symbol),顏色和名字,可以通過(guò)點(diǎn)擊圖例控制哪些系列不顯示,這篇文章主要給大家介紹了關(guān)于Echarts圖例組件的相關(guān)資料,需要的朋友可以參考下
    2021-06-06
  • webpack的pitching loader詳解

    webpack的pitching loader詳解

    這篇文章主要介紹了webpack的pitching loader詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • js實(shí)現(xiàn)簡(jiǎn)單的倒計(jì)時(shí)

    js實(shí)現(xiàn)簡(jiǎn)單的倒計(jì)時(shí)

    這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)簡(jiǎn)單的倒計(jì)時(shí),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-01-01

最新評(píng)論