[Express.js]next()函数的作用
<p>一、概述</p><p>Express.js所谓中间件,就是从接收到用户http请求开始到调用响应对象(res)之间的那段处理过程,它的本质是一个类似这样的回调函数:</p>
<div class="cnblogs_code">
<pre>(req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
}</span></pre>
</div>
<p>其中,req是请求对象,res是响应对象,next则是一个函数,调用它可以马上跳到下一个中间件回调函数。</p>
<p>一个匹配的路由可以存在很多个中间件,来作为该用户请求的处理过程,直至最终调用响应对象输出响应内容给用户,这个过程就要用到next函数:</p>
<div class="cnblogs_code">
<pre>app.use((req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"pos_01"<span style="color: rgba(0, 0, 0, 1)">);
next();
});
app.use((req, res, next) </span>=><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"pos_02"<span style="color: rgba(0, 0, 0, 1)">);
next();
});
app.get(</span>'/', (req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"pos_03"<span style="color: rgba(0, 0, 0, 1)">);
res.send(</span>'Hello World'<span style="color: rgba(0, 0, 0, 1)">);
});</span></pre>
</div>
<p>当用户访问站点根目录,以上代码首先会执行第一个app.use传入的“中间件”,再跳到第二个app.use传入的“中间件”,最后才执行app.get绑定的"中间件",在此中间件当中不再调用next(),所以它是处理请求的终点,在此调用响应对象输出响应内容给用户:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">服务器输出</span>
<span style="color: rgba(0, 0, 0, 1)">pos_01
pos_02
pos_03
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">浏览器输出</span>
Hello World</pre>
</div>
<p>总之,一个指定了多个中间件的路由,这些中间件当中,如果不指定next(),它是不会主动跳到下一个中间件的,执行完毕的中间件如果没有调用res.send()之类的方法,请求就会挂起:</p>
<div class="cnblogs_code">
<pre>route.use((req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"pos_01"<span style="color: rgba(0, 0, 0, 1)">);
});
route.get(</span>'/', (req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"pos_02"<span style="color: rgba(0, 0, 0, 1)">);
res.send(</span>'bird home page.'<span style="color: rgba(0, 0, 0, 1)">);
});</span></pre>
</div>
<p>由于第一个中间件不调用next(),所有符合此路由之下的剩余中间件都不会执行;</p>
<p>无论是任何方法调用的中间件都沿用该逻辑,以下两个例子都是执行了第一个中间件后,后续的路由即使符合匹配规则,也不会执行:</p>
<div class="cnblogs_code">
<pre>route.get('/', (req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"pos_01"<span style="color: rgba(0, 0, 0, 1)">);
});
route.get(</span>'/', (req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">不会执行</span>
console.log("pos_02"<span style="color: rgba(0, 0, 0, 1)">);
});</span></pre>
</div>
<div class="cnblogs_code">
<pre>const midd_1 = (req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"pos_01"<span style="color: rgba(0, 0, 0, 1)">);
};
const midd_2 </span>= (req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">不会执行</span>
console.log("pos_02"<span style="color: rgba(0, 0, 0, 1)">);
}
route.get(</span>'/'<span style="color: rgba(0, 0, 0, 1)">, );
route.get(</span>'/', (req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">不会执行</span>
console.log("pos_03"<span style="color: rgba(0, 0, 0, 1)">);
});</span></pre>
</div>
<p> </p>
<p>二、next不会终止当前函数栈,只是纯粹调用了下一个函数(return的重要性)</p>
<p>在回调函数中调用next()并不会影响该函数流程,它只是纯粹进入了另外一个函数栈,当它返回,仍会继续执行剩下的内容:</p>
<div class="cnblogs_code">
<pre>app.use((req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"pos_01"<span style="color: rgba(0, 0, 0, 1)">);
next();
console.log(</span>"pos_011"<span style="color: rgba(0, 0, 0, 1)">);
});
app.use((req, res, next) </span>=><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"pos_02"<span style="color: rgba(0, 0, 0, 1)">);
next();
});
app.get(</span>'/', (req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"pos_03"<span style="color: rgba(0, 0, 0, 1)">);
res.send(</span>'Hello World'<span style="color: rgba(0, 0, 0, 1)">);
});</span></pre>
</div>
<p>留意第一个app.use传入的“中间件”,它在调用next()后依然有后续逻辑,这些逻辑会等待next执行玩其他"中间件"后继续执行:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">服务器输出:</span>
<span style="color: rgba(0, 0, 0, 1)">pos_01
pos_02
pos_03
pos_011
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">浏览器输出:</span>
Hello World</pre>
</div>
<p>所以,我们应该在需要调用next()跳过当前中间件函数的后面加上return语句,防止它继续往后执行:</p>
<div class="cnblogs_code">
<pre>app.use((req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"pos_01"<span style="color: rgba(0, 0, 0, 1)">);
next();
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
console.log(</span>"pos_011"<span style="color: rgba(0, 0, 0, 1)">);
});</span></pre>
</div>
<p>在第一个app.use绑定的中间件处加上return,就能确保"pos_011"不会再输出;</p>
<p>三、next(param)</p>
<p>next()函数接受一个参数:</p>
<p>1.这个参数可以是一个Error对象,当参数为Error对象,传入这个对象的next()的行为将不是调用下一个"中间件"而是直接抛出错误并返回:</p>
<div class="cnblogs_code">
<pre>app.use((req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"pos_01"<span style="color: rgba(0, 0, 0, 1)">);
next();
console.log(</span>"pos_011"<span style="color: rgba(0, 0, 0, 1)">);
});
app.use((req, res, next) </span>=><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"pos_02"<span style="color: rgba(0, 0, 0, 1)">);
next(</span><span style="color: rgba(0, 0, 255, 1)">new</span> Error('failed to load user'<span style="color: rgba(0, 0, 0, 1)">));
});
app.get(</span>'/', (req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"pos_03"<span style="color: rgba(0, 0, 0, 1)">);
res.send(</span>'Hello World'<span style="color: rgba(0, 0, 0, 1)">);
});</span></pre>
</div>
<p>以上例子的app.get绑定的回调函数将不会执行,浏览器会输出来自next()的错误信息,但是"pos_011"依然会输出,因为next()接受的错误对象不中断流程的执行(这也许是因为回调函数在不同的tick的原因):</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">服务器输出:</span>
<span style="color: rgba(0, 0, 0, 1)">pos_01
pos_02
pos_011
Error: failed to load user
at file:</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">/mnt/hgfs/lroot/wwwroot/10003/test001/index04.js:19:8</span>
<span style="color: rgba(0, 0, 0, 1)"> ...
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">浏览器输出:</span>
<span style="color: rgba(0, 0, 0, 1)">Error: failed to load user
at file:</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">/mnt/hgfs/lroot/wwwroot/10003/test001/index04.js:19:8</span>
...</pre>
</div>
<p>2. next('route'),当参数为'route',next()的行为是跳过当前路由set的所有中间件,从下一个符合条件的路由继续(如果有的话):</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">第一个路由定义的一set中间件</span>
route.get('/', (req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"pos_01"<span style="color: rgba(0, 0, 0, 1)">);
next(</span>'route'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span>;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">记得加上return,防止后续路由运行完毕后返回运行此路由剩下的上下文</span>
console.log("pos_02"<span style="color: rgba(0, 0, 0, 1)">);
}, (req, res, next) </span>=><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"pos_03"<span style="color: rgba(0, 0, 0, 1)">);
});
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">第二个路由定义的中间件</span>
route.get('/', (req, res, next) =><span style="color: rgba(0, 0, 0, 1)"> {
res.send(</span>'bird home page.'<span style="color: rgba(0, 0, 0, 1)">);
console.log(</span>"pos_04"<span style="color: rgba(0, 0, 0, 1)">);
});</span></pre>
</div>
<p>Express将每个编写的app.METHOD(),route.METHOD()定义为一个独立的中间件set,这些方法的第二个以上参数,可以接受一个或多个函数作为只指定一个中间件,或指定一个数组,数组每一项都是函数作为指定多个中间件,总之,只要是指定了多个中间件函数的方法,都认为是中间件set,next('route')就是用来跳过这些中间件set的;</p>
<p>以上示例中,第一个route.METHOD()指定了中间件set,在第一个中间件执行到next('route')后,会直接跳过此数组剩余的中间件函数,直接去执行下一个route.METHOD()指定的中间件,所以"pos_03"是不会输出的,同时又因为加上了return,所以"pos_02"也不会输出,原因参考上文;</p>
<div class="otQkpb" data-animation-nesting="" data-sfc-cp="" data-sfc-cb="" data-complete="true" data-processed="true" data-sae="">注意事項</div>
<ul class="KsbFXc U6u95" data-sfc-cb="" data-complete="true" data-processed="true">
<li class="dF3vjf" data-sfc-cb="" data-hveid="CAEIDBAA" data-complete="true" data-sae=""><span class="T286Pc" data-sfc-cp="" data-sfc-cb="" data-complete="true"><strong class="Yjhzub" data-sfc-cb="" data-complete="true">僅限路由回呼</strong>:<code class="o8j0Mc" dir="ltr" data-sfc-cb="" data-complete="true" data-sae="">next('route')</code> 只在透過 <code class="o8j0Mc" dir="ltr" data-sfc-cb="" data-complete="true" data-sae="">app.METHOD()</code> 或 <code class="o8j0Mc" dir="ltr" data-sfc-cb="" data-complete="true" data-sae="">router.METHOD()</code> 載入的中間件函式中有效。</span></li>
<li class="dF3vjf" data-sfc-cb="" data-hveid="CAEIDBAB" data-complete="true" data-sae=""><span class="T286Pc" data-sfc-cp="" data-sfc-cb="" data-complete="true"><strong class="Yjhzub" data-sfc-cb="" data-complete="true">不要在 <code class="o8j0Mc" dir="ltr" data-sfc-cb="" data-complete="true" data-sae="">app.use()</code> 使用</strong>:在 <code class="o8j0Mc" dir="ltr" data-sfc-cb="" data-complete="true" data-sae="">app.use()</code> 中呼叫 <code class="o8j0Mc" dir="ltr" data-sfc-cb="" data-complete="true" data-sae="">next('route')</code> 的效果等同於普通的 <code class="o8j0Mc" dir="ltr" data-sfc-cb="" data-complete="true" data-sae="">next()</code>,不會跳過後續的中間件。</span></li>
</ul>
<p> </p>
<p>三、指定多个"中间件"但没调用next()</p>
<p>当某个路由指定了多个中间件,但是中途其中一个中间件没有调用next(),那么它将不会自动切换到下一个中间件,结果就是执行完当前中间件后挂起请求(如果这个中间件最终也没有调用响应对象输出内容的话):</p>
<div class="cnblogs_code">
<pre>const cb0 = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (req, res, next) {
console.log(</span>'CB0'<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">正常情况会调用next(),但是此处故意不调用</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">next()</span>
<span style="color: rgba(0, 0, 0, 1)">}
const cb1 </span>= <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (req, res, next) {
console.log(</span>'CB1'<span style="color: rgba(0, 0, 0, 1)">)
next()
}
const cb2 </span>= <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (req, res) {
res.send(</span>'Hello from C!'<span style="color: rgba(0, 0, 0, 1)">)
}
app.get(</span>'/', );</pre>
</div>
<p>结果就是,服务器输出"CB0",客户端请求挂起。</p>
<p> </p><br><br>
来源:https://www.cnblogs.com/yiyide266/p/19061166
頁:
[1]