我的理解是,中间件函数是一个路由处理程序,但它可能会调用下一个函数参数以将控制权传递给堆栈上的中间件函数。这是标准路由处理程序和中间件功能之间的唯一区别吗?
jfr*_*d00 12
中间件
作为通用术语,中间件是检查传入请求并准备它以供其他处理程序进一步处理或使处理短路(例如当它发现用户尚未通过身份验证时)的代码。一些例子:
会话管理。解析 cookie,查找会话 cookie,查找该 cookie 的会话状态并将会话信息添加到请求中,以便其他处理程序可以随时访问会话对象,而无需他们做任何额外的工作。在快递中,这将是express.session().
验证。 检查用户是否正在尝试访问需要身份验证的站点部分。如果是这样,请检查他们的身份验证凭据是否正确。如果不是,则发送错误响应并阻止进一步处理。如果是这样,请允许进一步处理。
Cookie 的解析。 将传入的 cookie 解析为易于使用的数据结构,请求处理程序可以轻松访问 cookie 数据,而无需每个人自行解析它们。这种类型的中间件内置于 Express 中并自动发生。
解析和读取 POST/PUT 正文。 如果传入请求是 POST 或 PUT,则请求正文可能包含处理请求所需的数据,并且需要从传入流中读取。中间件可以将读取主体的任务集中起来,然后根据数据类型对其进行解析,并将结果放入已知的请求参数中(在 Express 中,这将是req.body)。Express 有一些现成的中间件,用于使用express.json()或进行此类正文解析express.urlencoded()。类似的中间件库multer用于处理文件上传。
提供静态文件(HTML、CSS、JS 等)。 对于某些 URL 组,服务器所需要做的就是提供静态文件(未向文件添加自定义内容)。这对于 CSS 文件、JS 文件甚至一些 HTML 文件都很常见。Express 为此提供了中间件,称为express.static().
路由处理程序
作为通用术语,路由处理程序是查找对特定传入 URL(例如/login并且通常是特定 HTTP 动词(例如 POST))的请求的代码,并且具有用于处理该精确 URL 和动词的特定代码。一些例子:
提供特定的网页。 处理特定网页的浏览器请求。
处理特定的表单帖子。 例如,当用户登录站点时,会向服务器提交一个登录名。这将由 Express 中的请求处理程序处理,例如app.post("/login", ...).
响应特定的 API 请求。假设您有一个用于图书销售网站的 API。您可以在该 API 中提供通过 ISBN 号获取图书信息的能力。因此,您设计了一个 api 来支持对特定书籍的查询,例如书籍的 ISBN 号/api/book/list/0143105426在哪里0143105426(通用书籍标识符)。在这种情况下,你会创建一个快捷请求处理程序的URL看起来像如下: app.get('/api/book/list/:isbn', ...)。Express 中的请求处理程序然后可以以编程方式检查req.parms.isbn以获取请求的 isbn 编号,在数据库中查找它并返回所需的书籍信息。
因此,这些是任何语言的任何 Web 服务器系统中的中间件与请求处理程序的一般描述。
在 Express 中,两者之间没有硬性区别。有人通常会调用一些中间件来检查一堆不同的请求,并通常为进一步处理准备请求。有人通常会将某些东西称为针对特定 URL(或 URL 类型)的路由处理程序,其主要目的是向客户端发送该 URL 的响应。
但是,您对 Express 进行编程的方式,区别非常模糊。Express 提供处理路由的功能,例如:
app.use()
app.get()
app.post()
app.put()
app.delete()
app.all()
Run Code Online (Sandbox Code Playgroud)
其中任何一个都可以用于中间件或路由处理程序。哪个将调用给定的代码块与代码的总体意图有关,而不是它使用 Express 中的哪些工具。
更典型的,一个会使用app.use()中间件和app.get()和app.post()路由处理器。但是有一些用例可以与此不同,因为它实际上取决于特定情况和您要尝试做什么。
您甚至可以将多个处理程序传递给给定的路由定义,其中第一个是中间件,然后是路由处理程序。
app.get("/admin", verifyAuth, (req, res) => {
// process the /admin URL, auth is already verified
req.sendFile("...");
});
Run Code Online (Sandbox Code Playgroud)
中间件对于大量不同的请求处于活动状态是很常见的。例如,您可能有一个身份验证中间件,如果用户尚未登录,它会阻止访问 95% 的站点(除了一些一般信息页面,如主页、登录和帐户创建页面之外的所有内容)。
中间件对于所有 HTTP 动词(例如 GET、POST、DELETE、PUT 等)都处于活动状态也很常见……在 express 中,这通常是app.use()or app.all()。请求处理程序通常仅用于一个特定的动词,例如app.get()或app.post()。
您可能有会话中间件,它为站点上的每个请求加载一个会话对象(如果有的话),然后将控制权传递给其他处理程序,这些处理程序可以自行决定是否需要访问会话对象。
请求处理程序通常以特定 URL 为目标,并且仅针对该特定 URL 处于活动状态。例如,/loginURL 通常会有一个请求处理程序来呈现该特定页面或响应登录表单请求。
路径匹配app.use()不同
在 Express 中,还有另一个细微的区别。中间件通常指定为:
app.use(path, handler);
Run Code Online (Sandbox Code Playgroud)
并且,路由通常指定为:
app.get(path, handler);
app.post(path, handler);
app.put(path, handler);
// etc...
Run Code Online (Sandbox Code Playgroud)
app.use()app.get()在如何匹配路径方面比其他人更贪婪。 app.get()需要完全匹配。 app.use()部分匹配没问题。这里有些例子:
因此,对于 URL 请求/category:
app.use("/category", ...) matches
app.get("/category", ...) matches
Run Code Online (Sandbox Code Playgroud)
对于 URL 请求/category/fiction:
app.use("/category", ...) matches
app.get("/category", ...) does not match
Run Code Online (Sandbox Code Playgroud)
您可以看到它app.use()接受部分 URL 匹配,app.get()而它的其他表亲不接受部分 URL 匹配。
现在,当然,app.get()如果需要,您可以使用for 中间件,如果需要,也可以使用app.use()for 请求处理程序,但通常会使用app.use()中间件app.get()及其表兄弟的请求处理程序。
好吧,让我们先探讨一些定义。
\n明确文档中的定义:
\n\n\n中间件函数是可以访问请求对象 (req)、响应对象 (res) 以及 application\xe2\x80\x99s 请求-响应周期中的下一个函数的函数。下一个函数是 Express 路由器中的一个函数,当被调用时,它会执行当前中间件之后的中间件。
\n
明确文档的定义:
\n\n\n这些路由方法指定一个回调函数(有时称为 \xe2\x80\x9chandlerfunctions\xe2\x80\x9d),当应用程序收到对指定路由(端点)和 HTTP 方法的请求时调用。换句话说,应用程序 \xe2\x80\x9clistens\xe2\x80\x9d 查找与指定路由和方法匹配的请求,当检测到匹配时,它会调用指定的回调函数。
\n
这里的路由方法是从HTTP请求派生的方法以及all()匹配所有HTTP方法的方法。但提醒一下,该use()方法不是路由方法。让我们查看 Express 文档以进行澄清。
来自明确的文档:
\n\n\n路由方法派生自 HTTP 方法之一,并附加到 Express 类的实例。Express 支持与所有 HTTP 请求方法相对应的方法:get、post 等。有一个特殊的路由方法app.all(),用于在所有HTTP请求方法的路径上加载中间件函数。
\n
所以我们看到中间件函数和处理函数并不是彼此对立的。
\n中间件函数是带有额外参数的函数,next以及用于调用下一个中间件函数的req和。\n和其他 HTTP 派生方法(等)可以使用中间件函数。处理函数和中间件函数之间的区别在对象上是相同的。resapp.use()app.get()app.all()approuter
另一方面,处理函数是由路由方法指定的函数。\n因此,当任何路由方法req传递具有、的函数时res,next它既是中间件函数,又是处理函数。
但app.use()如果函数有req、res、 和next,那么它只是一个中间件函数。
现在,那些只有参数req和res没有next参数的函数呢?!
\n根据定义,它们不是中间件函数。\n如果在路由方法上使用此类函数,那么它们只是handler函数。handler当它是唯一的回调函数时,我们使用这样一个不是中间件的函数。因为在这种情况下,不需要next调用下一个中间件函数。
如果它们被使用,app.use()那么它们就不是中间件,也不是处理函数。
好的,定义已经够多了,但是路由方法middleware的功能app.use()和路由方法handler & middleware的功能之间有什么区别吗?
它们看起来很相似,因为两者都有next参数并且工作原理几乎相同。但有一个细微的差别。
next(\'route\')仅适用于函数handler & middleware。因此app.use()无法调用,next(\'route\')因为它们只能具有middleware函数。
根据快递文件:
\n\n\nnext(\'route\') 仅适用于使用 app.METHOD() 或 router.METHOD() 函数加载的中间件函数。
\nMETHOD 是中间件函数处理的请求的 HTTP 方法(例如 GET、PUT 或 POST),采用小写形式。
\n
如果您知道是什么next(\'route\'),那么答案就在这里为您完成:)。\n如果您不知道,您可以一起来。
那么让我们看看什么是next(\'route\').
从这里开始,我们将使用关键字 METHOD 而不是get、post、all等。
从前面的中间件函数定义中,我们看到app.use()或者app.METHOD()可以带几个中间件函数。
来自快递文档:
\n\n\n如果当前的中间件函数没有结束请求-响应周期,则必须调用 next() 将控制权传递给下一个中间件函数。否则,请求将被挂起。
\n
我们看到每个中间件函数必须调用下一个中间件函数或结束响应。
\n但有时在某些情况下,您可能想跳过当前路由的所有下一个中间件函数,但又不想立即结束响应。因为也许还有其他路由需要匹配。因此,要跳过当前路由的所有中间件函数而不结束响应,可以运行next(\'route\'). 它将跳过当前路由的所有回调函数并搜索以匹配下一个路由。
例如(来自明确文档):
\napp.get(\'/user/:id\', function (req, res, next) {\n // if the user ID is 0, skip to the next route\n if (req.params.id === \'0\') next(\'route\')\n // otherwise pass the control to the next middleware function in this stack\n else next()\n}, function (req, res, next) {\n // send a regular response\n res.send(\'regular\')\n})\n\n// handler for the /user/:id path, which sends a special response\napp.get(\'/user/:id\', function (req, res, next) {\n res.send(\'special\')\n})\nRun Code Online (Sandbox Code Playgroud)\n看,在某种情况下,(req.params.id === \'0\')我们想跳过下一个回调函数,但又不想结束响应,因为相同路径参数的另一个路由将被匹配,并且该路由将发送特殊响应。(是的,多次使用相同的路径参数是有效的METHOD。在这种情况下,所有路由都会被匹配,直到响应结束)。所以在这种情况下,我们运行next(\'route\')并跳过当前路由的所有回调函数。这里如果条件不满足,那么我们调用下一个回调函数。
此next(\'route\')行为仅在函数中可能app.METHOD()。
从明确的文件中回顾:
\n\n\nnext(\'route\') 仅适用于使用 app.METHOD() 或 router.METHOD() 函数加载的中间件函数。
\n
这意味着next(\'route\')只能从函数中调用它handler & middleware。唯一middleware的函数app.use()不能调用它。
由于在 中不可能跳过当前路由的所有回调函数app.use(),所以我们在这里应该小心。app.use()我们应该只使用在任何情况下都不需要跳过的中间件功能。因为我们要么必须结束响应,要么从头到尾遍历所有回调函数,所以我们根本不能跳过它们。
您可以访问这里了解更多信息
\n