express路由子路由器_使用Express在Node中构建您的第一个路由器

本文介绍了Express路由的基础知识,包括路由的定义、方法、路径和处理程序。通过实例展示了如何在Node.js中创建带有路由器的简单应用,包括创建路由器、添加用户身份验证,以及如何利用Express的路由功能组织Web应用的不同部分。

express路由子路由器

本文最初发布在Okta开发人员博客上 感谢您支持使SitePoint成为可能的合作伙伴。

如果最近几年您使用Node进行过任何Web开发,那么您可能已经使用过Express。 即使您没有直接使用它,许多意在使Web开发变得更加简单的框架仍然基于Express。

Express的主要功能之一就是能够创建路线。 URL的无限组合可以在同一台Express服务器上运行,而路由是确定哪些URL运行哪段代码的方式。 您可以具有参数和通配符,从而不必显式声明每个端点。

在本教程中,我将引导您创建服务器,并教您所有有关Express中路由的知识。

Express中的路线是什么?

路由确定在给定任何URL的情况下应传递哪些数据。 让我们以最基本的文件服务器为例。 假设您的文件结构为:

files/
├── images/
│   ├── cat.png
│   ├── dog.jpg
│   └── pig.bmp
└── text/
    ├── README.md
    └── todo.txt

然后,您可以运行一个简单的HTTP服务器,该服务器将自动提供这些文件并为目录创建索引。 没有files/index.html ,但是服务器仍在生成网页并根据该文件夹中的文件提供内容。 如果你去/images/cow.gif你会得到一个404错误-即使没有文件存在,它仍然煮好的东西

npm install -g http-server
cd files
http-server

文件服务器演示

在Express中,路由由methodpathhandler

方法,路径和处理程序,我的天哪!

method可以是任何HTTP动词,例如GET (用于获取内容-这是大多数网页使用的内容)或POST (用于将内容发送到服务器-这在HTML表单中很常见)。 如果选择,您还可以指定希望Express为所有方法处理相同的路径。

path是描述相对URL的字符串或正则表达式。 如果您使用的是应用程序的根目录,则说明绝对URL。 可以通过几种方式定义路径。

  • 简单字符串 :字符串'/'表示您要在路由器的根目录使用此路由。 字符串'/asdf'将覆盖路径/asdf
  • 通配符 :字符串还可以包含一些通配符,其作用与正则表达式相似,但有一些限制:
    • ? :A ? 说前一个字符是可选的。 路径'/Joh?n'将覆盖/Jon/John
    • + :A +表示前一个字符可以重复任意多次,但必须至少重复一次。 '/ni+ce'路径将覆盖/nice以及/niiiiiiiiiiiiiiiiice
    • * :A *表示前一个字符是可选的,可以根据需要多次重复。 路径'/wow!*'将匹配/wow/wow! ,甚至/wow!!!!!!!!!!!!
    • () :您还可以将通配符应用于一组字符。 '/(ha)+'将匹配/ha/haha/hahahahaha ,但不匹配/hah
  • 正则表达式 :如果您想超越基本通配符,则可以使用正则表达式。 使用/^\/(pen-)?((pine)?apple-)+pen$/您可以匹配/apple-pen/pineapple-pen/pen-pineapple-apple-pen
  • 参数 :另一个非常有用的功能是您可以在路径中包含参数。 这使您可以轻松地提供具有动态部分的RESTful URL。 路径'/posts/:postId'不仅会匹配/posts/42 ,而且请求还将包含一个params.postId变量,其值为'42'

方法和路径对于知道何时执行某项操作至关重要,但是处理程序是在这些情况下实际上会被调用的回调函数。 向处理程序传递一个request ,一个response和一个next回调,这些参数通常写为(req, res, next)

  • Request( req :请求包含有关用户要求的各种信息。 在这里,您可以访问路径,参数,标题和许多其他内容。 对于请求中的所有内容,您可以查阅API参考
  • 响应( res :响应是您将信息发送回用户的方式。 发送数据的最简单方法是使用.send方法(例如res.send('Hello, world!') ),但是还有许多其他方法。 同样,您可以在API参考中找到所有方法
  • Next Callback( nextnext函数允许您对同一路由使用多个处理程序。 您可以使用一个处理程序来处理信息,完成后可以调用next()表示可以继续使用下一个处理程序。 如果您传递一个字符串,它将抛出一个错误,您可以在其他地方捕获该错误,或​​者向用户显示(例如next('You must be authenticated to access this route') )。

什么是Express中的路由器?

现在,您对路由有了更多的了解,它与路由器有何不同? 您可以将路由器视为路由的集合。 这是组织应用程序不同部分的有用方法。

使用路由器时,即使您打算从某个子路径使用该路由器,也可以根据根路径进行思考。 例如,假设您有一个用于管理消息的API。 你可以有一个路径的路由器'/'GET所有消息或POST一个新的消息。 您可以使用另一个路径'/:id'GETPUT (编辑)特定消息。

然后,您的应用程序可以使用该路由器,并使用app.use('/messages', messageRouter)将其托管在/messages 。 路由器本身不必关心它的全局路径,甚至可以在多个路由中使用(例如/messages/texts/email )。

使用Express在Node中创建带有路由器的简单应用

讨论已经足够了……让我们看一些实际的代码。 首先,创建一个包含所有代码的文件夹。 然后设置一个package.json文件夹来帮助管理依赖项。 您可以使用npm init来执行此操作。 您还需要安装Express

mkdir my-first-router
cd my-first-router
npm init -y
npm install express@4.16.4 hbs@4.0.1

使用以下代码创建一个index.js文件:

index.js

const express = require('express')
const path = require('path')

const app = express()

app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'hbs')

app.get('/', (req, res) => {
  res.render('index', {
    title: 'Hello, world!',
    content: 'How are you?'
  })
})

const port = process.env.PORT || 3000
app.listen(port, () => console.log(`App listening on port ${port}`))

这告诉Express将把手hbs )用作视图引擎。 它使用Node的内置path来告诉它包含视图的目录。 告诉/路径使用index.hbs呈现页面,这会将content放在段落( p )标记中。

为确保Express具有要渲染的模板,请创建一个名为views的新文件夹,然后在其中创建一个名为layout.hbs的新文件。 当您告诉Express渲染视图时,它将首先渲染layout.hbs并将视图的内容放在{{{body}}}标签内。 这使您可以为应用设置框架。 这是一些使用Bootstrap的基本HTML,可为您提供漂亮的样式,而无需编写任何CSS。 这还将使title传递到/路由中的上下文中。

免费学习PHP!

全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。

原价$ 11.95 您的完全免费

views / layout.hbs

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">

    <title>{{title}}</title>
  </head>
  <body>
    <h1>{{title}}</h1>
    <main>
      {{{body}}}
    </main>
  </body>
</html>

您还需要创建一个index.hbs视图,该视图现在才是非常基本的:

views / index.hbs

<p>{{content}}</p>

为了使开发更加容易,您可以使用以下命令安装nodemon

npm install --save-dev nodemon@1.18.4

然后修改您的package.json文件,以便"scripts"条目包含带有nodemon .的启动脚本nodemon . 。 这样,您就可以简单地运行npm start并且无论何时进行更改,服务器都会自动重新启动:

"scripts": {
  "start": "nodemon ."
}

现在在终端中,如果您键入npm start ,则将启动服务器。 然后,您可以转到http://localhost:3000查看该应用程序正在运行。

文件服务器演示

在Express中创建路由器

好吧,这很无聊。 如何使其做些有用的事情? 让我们创建一个简单的待办事项清单。 首先创建一个路由器来管理项目列表。 新建一个名为todo.js文件:

todo.js

const express = require('express')

const router = express.Router()

let todo = []

router.post('/', (req, res, next) => {
  todo = [...req.body.todo || []]
  if (req.body.remove) todo.splice(req.body.remove, 1)
  if (req.body.new) todo.push({})

  next()
})

router.use('/', (req, res) => {
  res.render('todo', { title: 'To-do list', todo })
})

module.exports = router

在这里,您有两个路由处理程序。 第一个侦听POST请求(由router.post表示)。 它将用从表单收到的所有内容的副本替换待办事项列表。 如果表单包含remove属性(包含索引),它将使用splice删除该索引处的元素。 如果表单包含new属性,则将新项推入数组。 修改待办事项列表后,它将调用next()继续进行下一个路由处理程序。

始终使用第二个路由处理程序(由router.use表示)。 它的唯一目的是呈现待办事项列表。 通过这样分离路由,您可以轻松地始终执行一件事,而仅在某些情况下(在这种情况下,针对POST请求)可以轻松地执行另一件事。

要告诉应用程序使用此路由器,您必须在index.js添加几行:

index.js

@@ -1,11 +1,15 @@
 const express = require('express')
 const path = require('path')
+const todoRouter = require('./todo')

 const app = express()

 app.set('views', path.join(__dirname, 'views'))
 app.set('view engine', 'hbs')

+app.use(express.urlencoded({ extended: true }))
+app.use('/todo', todoRouter)
+
 app.get('/', (req, res) => {
   res.render('index', {
     title: 'Hello, world!',

现在为todo模板。 它更大一点,所以我最后保存了它。 如果您熟悉HTML,它应该很不错。 把手添加了一些使您可以访问变量的功能。 在这种情况下,如果没有任何项目,您将使用{{#if}}块来呈现特殊的东西,并且使用{{#each}}块来以最小的标记来呈现每个列表项。

此处使用的唯一JavaScript是在您更改某些内容时自动提交表单。 如果禁用了JavaScript,则由于标记了“自动保存”的隐藏按钮,因此在按键盘上的“ Enter”键时仍然可以使用。

views / todo.hbs

<form method="post">
  <div class="row">
    <div class="col">
      <button hidden>Autosave</button>
      <button class="btn btn-success" name="new" value="true">New</button>
    </div>
  </div>
  <div class="row mt-3">
    <div class="col">
      {{#if todo.length}}
        <ul class="list-group">
          {{#each todo}}
            <li class="list-group-item d-flex align-items-center">
              <input
                type="checkbox"
                onchange="this.form.submit()"
                name="todo[{{@index}}][checked]"
                {{#if this.checked}}checked{{/if}}
              />
              <input
                name="todo[{{@index}}][text]"
                onchange="this.form.submit()"
                class="form-control mx-2"
                value="{{this.text}}"
              />
              <button class="btn btn-danger" name="remove" value="{{@index}}">Remove</button>
            </li>
          {{/each}}
        </ul>
      {{else}}
        <h5>Your To-Do List is empty</h5>
      {{/if}}
    </div>
  </div>
  <style>
    input[type=checkbox]:checked + input {
      text-decoration: line-through;
      opacity: 0.75;
    }
  </style>
</form>

现在转到http://localhost:3000/todo并将一些项目输入到您的待办事项列表中。

购买牛奶

在节点中添加用户身份验证

现在,您有了功能齐全的工作清单。 您可能已经注意到,只有当您希望每个使用它的人共享同一列表时,此方法才起作用。 如果添加身份验证,则可以为每个用户提供单独的待办事项列表。

添加用户不必费劲。 实际上,只需使用Okta即可完成。 什么是Okta? ,您可能会问。 Okta是一项云服务,允许开发人员创建,编辑和安全地存储用户帐户和用户帐户数据,并将它们与一个或多个应用程序连接。

如果您还没有一个,请注册一个永久性的开发者帐户

您将需要保存一些信息以在应用程序中使用。 创建一个名为.env的新文件。 在其中输入您的组织网址。

HOST_URL=http://localhost:3000
OKTA_ORG_URL=https://{yourOktaOrgUrl}

您还需要一个随机字符串作为会话的“应用程序秘密”。 您可以使用以下命令生成它:

echo -e "\nAPP_SECRET=`npx -q uuid`" >> .env

接下来,登录到开发人员控制台,导航至“ 应用程序” ,然后单击“ 添加应用程序” 。 选择“ Web” ,然后单击“ 下一步” 。 为您的应用程序命名,例如“我的第一个路由器”。 将基本URI更改为http://localhost:3000/ ,并将登录重定向URI更改为http://localhost:3000/authorization-code/callback ,然后单击完成。

点击编辑并添加http://localhost:3000/注销重定向URL ,然后点击保存

Okta应用程序设置

创建应用程序后进入的页面包含需要保存到.env文件的更多信息。 复制客户ID和客户机密。

OKTA_CLIENT_ID={yourClientId}
OKTA_CLIENT_SECRET={yourClientSecret}

现在回到代码。 您需要添加Okta的OIDC中间件来控制身份验证。 它还依赖于使用会话。 您将需要使用dotenv.env文件中读取变量。 要安装所需的依赖项,请运行以下命令:

npm install @okta/oidc-middleware@1.0.1 dotenv@6.1.0 express-session@1.15.6

现在修改您的index.js文件。 在这里,您将添加会话和OIDC中间件,以及logout路径,以便用户可以注销该应用程序。 您还正在向todoRouter添加专门的中间件( app.use('/todo', oidc.ensureAuthenticated(), todoRouter) )。 通过添加oidc.ensureAuthenticated() ,可以让Okta确保只有在用户登录后才能访问该路由。如果用户未登录并尝试访问该路由,则将其转到一个安全的网站登录,然后重定向到您的网站。

index.js

@@ -1,14 +1,46 @@
+require('dotenv').config()
+
 const express = require('express')
 const path = require('path')
+const session = require('express-session')
+const { ExpressOIDC } = require('@okta/oidc-middleware')
+
 const todoRouter = require('./todo')

+const oidc = new ExpressOIDC({
+  issuer: `${process.env.OKTA_ORG_URL}/oauth2/default`,
+  client_id: process.env.OKTA_CLIENT_ID,
+  client_secret: process.env.OKTA_CLIENT_SECRET,
+  redirect_uri: `${process.env.HOST_URL}/authorization-code/callback`,
+  scope: 'openid profile'
+})
+
 const app = express()

+app.use(session({
+  secret: process.env.APP_SECRET,
+  resave: true,
+  saveUninitialized: false
+}))
+app.use(oidc.router)
+
 app.set('views', path.join(__dirname, 'views'))
 app.set('view engine', 'hbs')

 app.use(express.urlencoded({ extended: true }))
-app.use('/todo', todoRouter)
+app.use('/todo', oidc.ensureAuthenticated(), todoRouter)
+
+app.get('/logout', (req, res) => {
+  if (req.userContext) {
+    const idToken = req.userContext.tokens.id_token
+    const to = encodeURI(process.env.HOST_URL)
+    const params = `id_token_hint=${idToken}&post_logout_redirect_uri=${to}`
+    req.logout()
+    res.redirect(`${process.env.OKTA_ORG_URL}/oauth2/default/v1/logout?${params}`)
+  } else {
+    res.redirect('/')
+  }
+})

 app.get('/', (req, res) => {
   res.render('index', {

为了使用户注销时的操作变得容易一些,请从主页添加指向待办事项列表的链接。

views / index.hbs

<p>{{content}}</p>
<a href="/todo">Go to To-Do List</a>

您还可以在您的layout.hbs添加欢迎消息和注销按钮。

views / layout.hbs

@@ -12,6 +12,12 @@
   </head>
   <body class="container">
     <h1>{{title}}</h1>
+    {{#if userinfo}}
+      <h4>
+        Welcome back, {{userinfo.given_name}}!
+        <small><a href="/logout">Click here to log out</a></small>
+      </h4>
+    {{/if}}
     <main>
       {{{body}}}
     </main>

为此,您需要在呈现视图时将userinfo添加到上下文中。

todo.js

--- a/todo.js
+++ b/todo.js
@@ -13,7 +13,7 @@ router.post('/', (req, res, next) => {
 })

 router.use('/', (req, res) => {
-  res.render('todo', { title: 'To-do list', todo })
+  res.render('todo', { title: 'To-do list', todo, userinfo: req.userContext.userinfo })
 })

 module.exports = router

index.js

@@ -43,7 +43,10 @@ app.get('/logout', (req, res) => {
 })

 app.get('/', (req, res) => {
+  const { userinfo } = req.userContext || {}
+
   res.render('index', {
+    userinfo,
     title: 'Hello, world!',
     content: 'How are you?'
   })

好的,所以现在您需要用户登录才能编辑待办事项列表,但这仍然是一个共享列表。 为了将其分为每个用户一个单独的列表,请对todo.js进行另一个小的更改。

todo.js

@@ -2,17 +2,21 @@ const express = require('express')

 const router = express.Router()

-let todo = []
+const todosByUser = {}

 router.post('/', (req, res, next) => {
-  todo = [...req.body.todo || []]
+  const todo = [...req.body.todo || []]
   if (req.body.remove) todo.splice(req.body.remove, 1)
   if (req.body.new) todo.push({})

+  todosByUser[req.userContext.userinfo.sub] = todo
+
   next()
 })

 router.use('/', (req, res) => {
+  const todo = todosByUser[req.userContext.userinfo.sub] || []
+
   res.render('todo', { title: 'To-do list', todo, userinfo: req.userContext.userinfo })
 })

多用户演示

了解有关节点,快速和安全Web开发的更多信息

现在您有了功能齐全的待办事项清单,我鼓励您对其进行扩展。 尝试将数据存储在数据库中,甚至让Okta为您存储数据! 查看是否可以创建更多路由器以添加到Web服务器。

如果您想查看最终的代码示例,可以在GitHub上找到它。

如果您想了解有关Node和Express的更多信息,请查看Okta开发人员博客上的其他一些文章:

如果您对此帖子有任何疑问,请在下面添加评论。 有关更多精彩内容, 在Twitter上关注@oktadev在Facebook上关注我们,或订阅我们的YouTube频道

翻译自: https://www.sitepoint.com/build-your-first-router-in-node-with-express/

express路由子路由器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值