简体   繁体   English

ExpressJS 中的路由处理程序和中间件 function 有什么区别?

[英]What is the difference between a route handler and middleware function in ExpressJS?

My understanding is that a middleware function is a route handler with the exception that it may invoke the next function parameter to pass control on to the middleware function on the stack.我的理解是中间件 function 是一个路由处理程序,但它可能会调用下一个 function 参数以将控制权传递给堆栈上的中间件 ZC1C425268E68385D1AB5074C17A94F14。 Is this the only difference between a standard route handler and a middleware function?这是标准路由处理程序和中间件 function 之间的唯一区别吗?

Most of what you're talking about is semantics.你所说的大部分是语义。 In ExpressJS, middleware can be a route handler or a route handler can behave as middleware.在 ExpressJS 中,中间件可以是路由处理程序,或者路由处理程序可以充当中间件。 So, there is not a hard and fast line between the two.因此,两者之间并没有硬性界限。 But, when people refer to middleware or a route handler in a programming discussion, they usually mean something slightly different for each...但是,当人们在编程讨论中提到中间件或路由处理程序时,它们的含义通常略有不同......

Middleware中间件

As generic terms, middleware is code that examines an incoming request and prepares it for further processing by other handlers or short circuits the processing (like when it discovered the user is not authenticated yet).作为通用术语,中间件是检查传入请求并准备其以供其他处理程序进一步处理或使处理短路(例如当它发现用户尚未经过身份验证时)的代码。 Some examples:一些例子:

  • Session Management. Session 管理。 Parse cookies, look for session cookie, lookup session state for that cookie and add session info to the request so that other handlers down the line have ready access to the session object without any additional work on their part. Parse cookies, look for session cookie, lookup session state for that cookie and add session info to the request so that other handlers down the line have ready access to the session object without any additional work on their part. In express, this would be express.session() .在 express 中,这将是express.session()

  • Authentication.验证。 Check if the user is trying to access a portion of the site that requires authentication.检查用户是否正在尝试访问需要身份验证的站点部分。 If so, check if their authentication credentials are good.如果是这样,请检查他们的身份验证凭据是否良好。 If not, send an error response and prevent further processing.如果不是,则发送错误响应并阻止进一步处理。 If so, allow further processing.如果是这样,允许进一步处理。

  • Parsing of Cookies. Cookies的解析。 Parse incoming cookies into an easy-to-use data structure a request handler can have easy access to cookie data without each having to parse them on their own.将传入的 cookies 解析为易于使用的数据结构,请求处理程序可以轻松访问 cookie 数据,而无需各自解析它们。 This type of middleware is built into Express and happens automatically.这种类型的中间件内置于 Express 中并自动发生。

  • Parsing and Reading of POST/PUT bodies.解析和读取 POST/PUT 主体。 If the incoming request is a POST or PUT, the body of the request may contain data that is needed for processing the request and needs to be read from the incoming stream.如果传入请求是 POST 或 PUT,则请求的主体可能包含处理请求所需的数据,并且需要从传入的 stream 中读取。 Middleware can centralize this task reading the body, then parsing it according to the data type and putting the result into a known request parameter (in Express this would be req.body ).中间件可以集中读取主体的这个任务,然后根据数据类型对其进行解析并将结果放入一个已知的请求参数中(在 Express 中这将是req.body )。 Express has some ready-to-use middleware for this type of body parsing with express.json() or express.urlencoded() . Express 有一些现成的中间件,用于使用express.json()express.urlencoded()进行这种类型的正文解析。 A middleware library like multer is for handling file uploads.multer这样的中间件库用于处理文件上传。

  • Serve static files (HTML, CSS, JS, etc...).提供 static 文件(HTML、CSS、JS 等...)。 For some groups of URLs, all the server needs to do is to serve a static file (no custom content added to the file).对于某些 URL 组,服务器需要做的就是提供 static 文件(文件中没有添加自定义内容)。 This is common for CSS files, JS files and even some HTML files.这对于 CSS 文件、JS 文件甚至一些 HTML 文件很常见。 Express provides middleware for this which is called express.static() . Express 为此提供了中间件,称为express.static()

Route Handler路由处理程序

As a generic term, a route handler is code that is looking for a request to a specific incoming URL such as /login and often a specific HTTP verb such as POST and has specific code for handling that precise URL and verb.作为一个通用术语,路由处理程序是寻找对特定传入 URL(例如/login )和通常是特定 HTTP 动词(例如 POST)的请求的代码,并且具有用于处理该精确 ZE6B391A8D2C4D457032A 的特定代码。 Some examples:一些例子:

  • Serve a specific web page.提供特定的 web 页面。 Handle a browser request for a specific web page.处理特定 web 页面的浏览器请求。

  • Handle a specific form post.处理特定的表单帖子。 For example, when the user logs into the site, a login for is submitted to the server.例如,当用户登录站点时,会向服务器提交登录信息。 This would be handled by a request handler in Express such as app.post("/login", ...) .这将由 Express 中的请求处理程序处理,例如app.post("/login", ...)

  • Respond to a specific API request .响应特定的 API 请求 Suppose you had an API for a book selling web-site.假设您有一个图书销售网站的 API。 You might provide in that API the ability to get info on a book by its ISBN number.您可以在 API 中提供通过 ISBN 号获取图书信息的能力。 So, you design an api that supports a query for a particular book such as /api/book/list/0143105426 where 0143105426 is the ISBN number for the book (a universal book identifier).因此,您设计了一个 api,它支持对特定图书的查询,例如/api/book/list/0143105426 ,其中0143105426是图书的 ISBN 号(通用图书标识符)。 In that case, you'd create a request handler in Express for a URL that looks like that: app.get('/api/book/list/:isbn', ...) .在这种情况下,您将在 Express 中为 URL 创建一个请求处理程序,如下所示: app.get('/api/book/list/:isbn', ...) The request handler in Express could then programmatically examine req.parms.isbn to get the request isbn number, look it up in the database and return the desired info on the book. Express 中的请求处理程序可以通过编程检查req.parms.isbn以获取请求的 isbn 编号,在数据库中查找并返回所需的书籍信息。

So, those are somewhat generic descriptions of middleware vs. request handlers in any web server system in any language.因此,这些是任何语言的任何 web 服务器系统中的中间件与请求处理程序的通用描述。


In Express, there is no hard and fast distinction between the two.在 Express 中,两者之间没有硬性区别。 Someone would generally call something middleware that examines a bunch of different requests and usually prepares the request for further processing.有人通常会调用一些中间件来检查一堆不同的请求并通常准备请求以进行进一步处理。 Someone would generally call something a route handler that is targeted at a specific URL (or type of URL) and whose main purpose is to send a response back to the client for that URL.有人通常会称其为针对特定 URL(或 URL 类型)的路由处理程序,其主要目的是将响应发送回该 URL 的客户端。

But, the way you program Express, the distinction is pretty blurry.但是,按照您对 Express 的编程方式,区别非常模糊。 Express offers features for handling routes such as: Express 提供处理路线的功能,例如:

app.use()
app.get()
app.post()
app.put()
app.delete()
app.all()

Anyone of these can be used for either middleware or a route handler.这些中的任何一个都可以用于中间件或路由处理程序。 Which would would call a given block of code has more to do with the general intent of the code than exactly which tools in Express it uses.哪个会调用给定的代码块与代码的一般意图有关,而不是它使用 Express 中的确切工具。

More typically, one would use app.use() for middleware and app.get() and app.post() for route handlers.更典型的是,将app.use()用于中间件,将app.get()app.post()用于路由处理程序。 But there are use cases for doing it differently than that as it really depends upon the particular situation and what you're trying to do.但是有一些用例与此不同,因为它实际上取决于特定情况和您要做什么。

You can even pass more than one handler to a given route definition where the first one is middleware and followed by a route handler.您甚至可以将多个处理程序传递给给定的路由定义,其中第一个是中间件,然后是路由处理程序。

app.get("/admin", verifyAuth, (req, res) => {
     // process the /admin URL, auth is already verified
     req.sendFile("...");
});

It is common for middleware to be active for a large number of different requests.中间件对大量不同的请求处于活动状态是很常见的。 For example, you might have an authentication middleware that prevents access to 95% of the site if the user isn't already logged in (say everything except the a few generally information pages such as the homepage and the login and account creation pages).例如,您可能有一个身份验证中间件,如果用户尚未登录(例如除了一些一般信息页面,如主页和登录和帐户创建页面之外的所有内容),则该中间件会阻止访问 95% 的站点。

It is also common to have middleware that is active for all HTTP verbs such as GET, POST, DELETE, PUT, etc... In express, this would usually be app.use() or app.all() .拥有对所有 HTTP 动词(如 GET、POST、DELETE、PUT 等)都有效的中间件也很常见……在 express 中,这通常是app.use()app.all() Request handlers are usually only for one particular verb such as app.get() or app.post() .请求处理程序通常只针对一个特定的动词,例如app.get()app.post()

You might have session middleware that loads a session object (if one is available) for every single request on the site and then passes control on to other handlers that can, themselves, decide whether they need to access the session object or not. You might have session middleware that loads a session object (if one is available) for every single request on the site and then passes control on to other handlers that can, themselves, decide whether they need to access the session object or not.

It is common for request handlers to be targeted at a specific URL and only active for that specific URL.请求处理程序通常针对特定的 URL 并且仅针对该特定的 URL 有效。 For example, the /login URL would typically have one request handler that renders that particular page or responds to login form requests.例如, /login URL 通常会有一个请求处理程序来呈现该特定页面或响应登录表单请求。


Path Matching for app.use() is Different app.use()的路径匹配不同

In Express, there's one other subtle difference.在 Express 中,还有另一个细微的差别。 Middleware is typically specified with:中间件通常指定为:

app.use(path, handler);

And, a route is typically specified with:而且,路线通常指定为:

app.get(path, handler);
app.post(path, handler);
app.put(path, handler);
// etc...

app.use() is slightly more greedy than app.get() and the others in how it matches the path. app.use()在如何匹配路径方面比app.get()和其他人稍微贪婪一些。 app.get() requires a full match. app.get()需要完全匹配。 app.use() is OK with a partial match. app.use()可以部分匹配。 Here are some examples:这里有些例子:

So, for a URL request /category :因此,对于 URL 请求/category

 app.use("/category", ...)    matches
 app.get("/category", ...)    matches
 

For a URL request /category/fiction :对于 URL 请求/category/fiction

 app.use("/category", ...)    matches
 app.get("/category", ...)    does not match

You can see that app.use() accepts a partial URL match, app.get() and it's other cousins do not accept a partial URL match.您可以看到app.use()接受部分 URL 匹配, app.get()和它的其他表亲不接受部分 URL 匹配。

Now, of course, you can use app.get() for middleware if you want and can use app.use() for request handlers if you want, but typically one would use app.use() for middleware and app.get() and its cousins for request handlers.现在,当然,如果需要,您可以将app.get()用于中间件,并且可以将app.use()用于请求处理程序,但通常会使用app.use()作为中间件和app.get()及其用于请求处理程序的表亲。

Ok, let's explore some definitions first.好的,让我们先探索一些定义。

Middleware function中间件 function

definition from the express documentation :快速文档中的定义:

Middleware functions are functions that have access to the request object (req), the response object (res), and the next function in the application's request-response cycle.中间件函数是可以访问请求 object (req)、响应 object (res) 以及应用程序请求-响应周期中的下一个 function 的函数。 The next function is a function in the Express router which, when invoked, executes the middleware succeeding the current middleware.下一个 function 是 Express 路由器中的 function,当被调用时,它将在当前中间件之后执行中间件。

Handler function or callback function处理程序 function 或回调 function

definition from express Documentation :来自快递文档的定义:

These routing methods specify a callback function (sometimes called “handler functions”) called when the application receives a request to the specified route (endpoint) and HTTP method.这些路由方法指定了当应用程序接收到对指定路由(端点)的请求时调用的回调 function(有时称为“处理程序函数”)和 HTTP 方法。 In other words, the application “listens” for requests that match the specified route(s) and method(s), and when it detects a match, it calls the specified callback function.换句话说,应用程序“监听”匹配指定路由和方法的请求,当它检测到匹配时,它调用指定的回调 function。

Here Routing methods are the methods derived from HTTP requests and the all() method that matches all the HTTP methods.这里的路由方法是从 HTTP 请求派生的方法和匹配所有 HTTP 方法的all()方法。 But remind, the use() method is not a routing method .但请注意, use()方法不是路由方法 Let's go to express documentation for clarification.让我们 go 来表达文档以进行澄清。

Routing Methods路由方法

From express documentation :来自快递文档

A route method is derived from one of the HTTP methods and is attached to an instance of the express class.路由方法派生自 HTTP 方法之一,并附加到 express class 的实例。 Express supports methods that correspond to all HTTP request methods: get, post, and so on. Express 支持与所有 HTTP 请求方法对应的方法:get、post 等。 There is a special routing method, app.all(), used to load middleware functions at a path for all HTTP request methods.有一个特殊的路由方法,app.all(),用于在所有 HTTP 请求方法的路径上加载中间件函数。

So we see that middleware functions and handler functions are not opposite of each other.所以我们看到中间件函数和处理函数并不是相互对立的。

Middleware functions are functions that take an extra argument, next along with req and res which is used to invoke the next middleware function.中间件函数是带有额外参数的函数, next以及reqres用于调用下一个中间件 function。
app.use() and the other HTTP derived methods( app.get() , app.all() etc) can use middleware functions. app.use()和其他 HTTP 派生方法( app.get()app.all()等)可以使用中间件函数。 The difference between handler function and middleware function is same on both app and router object.处理程序 function 和中间件 function 之间的区别在approuter object 上是相同的。

On the other hand, a handler function is a function that is specified by the routing methods .另一方面,处理程序 function 是由路由方法指定的 function 。 So when any of the routing methods pass a function that has req , res , and next then it is both a middleware function and a handler function .因此,当任何路由方法通过具有reqresnext的 function 时,它既是中间件 function又是处理程序 ZC1C425268E68385D1AB4Z5074C17A94F

But for app.use() if the function has req , res , and next , then it is only a middleware function.但是对于app.use()如果 function 有reqresnext ,那么它只是一个中间件 function。

Now, what about the functions which only have req and res don't have the next argument?!现在,那些只有reqres没有next参数的函数呢?!
They are not middleware functions by definition.根据定义,它们不是中间件功能。 If such function is used on routing methods then they are only handler functions.如果这样的 function 用于路由方法,那么它们只是handler函数。 We use such a handler function which is not a middleware when it is the only one callback function.当它是唯一一个回调 function 时,我们使用这样的handler function 不是中间件。 Because in such cases there is no need for next which calls the next middleware function.因为在这种情况下,不需要next调用下一个中间件 function。

If they are used on app.use() then they are not middleware, nor handler function.如果它们在app.use()上使用,则它们不是中间件,也不是处理程序 function。

Ok, enough of the definitions, But is there any difference between middleware functions of app.use() and handler & middleware functions of routing methods ??好的,足够的定义,但是app.use()的中间件函数和路由方法handler & middleware middleware之间有什么区别吗?

They look similar since both have the next argument and works almost the same.它们看起来很相似,因为它们都有next参数并且工作方式几乎相同。 But there is a subtle difference.但是有一个细微的差别。

The next('route') works only on handler & middleware functions. next('route')仅适用于handler & middleware功能。 So app.use() can not invoke next('route') since they can have only middleware functions.所以app.use()不能调用next('route')因为它们只能有middleware功能。

According to express Documentation :根据快递文档

next('route') will work only in middleware functions that were loaded by using the app.METHOD() or router.METHOD() functions. next('route') 仅适用于使用 app.METHOD() 或 router.METHOD() 函数加载的中间件函数。

METHOD is the HTTP method of the request that the middleware function handles (such as GET, PUT, or POST) in lowercase. METHOD 是中间件 function 处理(如 GET、PUT 或 POST)的请求的 HTTP 方法,小写。

If you know what is next('route') then the answer is finished here for you:).如果您知道下一步是什么next('route') ,那么答案就在这里完成了:)。 In case you don't, you can come along.如果你没有,你可以一起来。

next('route')下一个('路线')

So let's see what is next('route') .那么让我们看看next('route')是什么。

From here we will use the keyword METHOD instead of get , post , all , etc.从这里我们将使用关键字 METHOD 代替getpostall等。

From the previous middleware function definition, we see app.use() or app.METHOD() can take several middleware functions.从前面的中间件 function 的定义中,我们看到app.use()app.METHOD()可以带几个中间件函数。

From the express documentation :快递文档中:

If the current middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function.如果当前中间件 function 没有结束请求-响应周期,则必须调用 next() 将控制权传递给下一个中间件 function。 Otherwise, the request will be left hanging.否则,请求将被挂起。

We see each middleware functions have to either call the next middleware function or end the response.我们看到每个中间件函数必须要么调用下一个中间件 function 要么结束响应。

But sometimes in some conditions, you may want to skip all the next middleware functions for the current route but also don't want to end the response right now.但有时在某些情况下,您可能希望跳过当前路由的所有下一个中间件功能,但也不想立即结束响应。 Because maybe there are other routes which should be matched.因为也许还有其他路线应该匹配。 So to skip all the middleware functions of the current route without ending the response, you can run next('route') .所以要跳过当前路由的所有中间件函数而不结束响应,你可以运行next('route') It will skip all the callback functions of the current route and search to match the next routes.它将跳过当前路由的所有回调函数并搜索以匹配下一个路由。

For Example (From express documentation ):例如(来自快速文档):

app.get('/user/:id', function (req, res, next) {
  // if the user ID is 0, skip to the next route
  if (req.params.id === '0') next('route')
  // otherwise pass the control to the next middleware function in this stack
  else next()
}, function (req, res, next) {
  // send a regular response
  res.send('regular')
})

// handler for the /user/:id path, which sends a special response
app.get('/user/:id', function (req, res, next) {
  res.send('special')
})

See, here in a certain condition (req.params.id === '0') we want to skip the next callback function but also don't want to end the response because there is another route of the same path parameter which will be matched and that route will send a special response.看,这里在特定条件下(req.params.id === '0')我们想跳过下一个回调 function 但也不想结束响应,因为有相同路径参数的另一条路由将匹配,该路由将发送特殊响应。 (Yeah, it is valid to use the same path parameter for the same METHOD several times. In such cases, all the routes will be matched until the response ends). (是的,对同一个METHOD多次使用同一个路径参数是有效的。在这种情况下,所有路由都会匹配到响应结束)。 So in such cases, we run the next('route') and all the callback function of the current route is skipped.所以在这种情况下,我们运行next('route')并跳过当前路由的所有回调 function。 Here if the condition is not met then we call the next callback function.这里如果条件不满足,那么我们调用下一个回调 function。

This next('route') behavior is only possible in the app.METHOD() functions.这种next('route')行为只能在app.METHOD()函数中实现。

Recalling from express documentation :快速文档中回顾:

next('route') will work only in middleware functions that were loaded by using the app.METHOD() or router.METHOD() functions. next('route') 仅适用于使用 app.METHOD() 或 router.METHOD() 函数加载的中间件函数。

That means this next('route') can be invoked only from handler & middleware functions.这意味着这个next('route')只能从handler & middleware函数中调用。 The only middleware functions of app.use() can not invoke it. app.use()唯一的middleware函数不能调用它。

Since skipping all callback functions of the current route is not possible in app.use() , we should be careful here.由于在app.use()中跳过当前路由的所有回调函数是不可能的,所以在这里我们应该小心。 We should only use the middleware functions in app.use() which need not be skipped in any condition.我们应该只使用app.use()中的中间件函数,在任何情况下都不需要跳过。 Because we either have to end the response or traverse all the callback functions from beginning to end, we can not skip them at all.因为我们要么必须结束响应,要么从头到尾遍历所有的回调函数,我们根本不能跳过它们。

You may visit here for more information您可以访问这里了解更多信息

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM