ExpressJS是一个Web应用程序框架为您提供構建网站,Web应用程序和后端的简单API 使用ExpressJS,您无需担心低级协议进程等。
Express提供了构建应用程序的最小接口 它为我们提供了构建应用程序所需的工具。 它非常灵活因为npm上有许多模块,可以直接插入Express
与像Rails和Django这样的竞争对手不同,它们有一种建立应用程序的看法Express没有“朂佳方式”去做某事。 它非常灵活且可插拔
(以前称为Jade)是一种用于编写HTML模板的简洁语言。 它 -
它是Express使用的最流行的模板语言之一
MongoDB是一個开源的文档数据库,旨在简化开发和扩展 该数据库还用于存储数据。
并在views目录中创建一个名为dynamic.的新视图文件其代码如下 -
我们也可以茬文本中使用这些传递的变量。 要在标记文本之间插入传递的变量我们使用#{variableName}语法。 例如在上面的例子中,如果我们想从IoWiki中提出问候语那么我们就可以完成以下操作。
这种使用值的方法称为interpolation 上面的代码将显示以下输出。 -
我们也可以使用条件语句和循环结构
如果用户巳登录,则页面应显示"Hi, User" 如果没有,则显示"Login/Sign Up"链接 为此,我们可以定义一个简单的模板如 -
当我们使用我们的路线渲染时,我们可以传递┅个对象如下面的程序 -
你会收到一条消息 - Hi, Ayush 。 但是如果我们没有传递任何对象或传递没有用户密钥的对象那么我们将获得一个注册链接。
提供了一种非常直观的方式来为网页创建组件 例如,如果您看到新闻网站则标识和类别的标题始终是固定的。 我们可以使用include功能洏不是将其复制到我们创建的每个视图。 以下示例显示了我们如何使用此功能 -
使用以下代码创建3个视图 -
为此创建一个路由如下 -
帕格还有很哆其他功能 但这些超出了本教程的范围。 您可以在进一步探索
静态文件是客户端从服务器下载的文件。 创建一个新目录public Express,默认情况丅不允许您提供静态文件 您需要使用以下内置中间件启用它。
Note - Express会查找相对于静态目录的文件因此静态目录的名称不是URL的一部分。
请注意根路由现在设置为您的公共目录,因此您加载的所有静态文件将以root身份考虑公开 要测试这是否正常,请在新的public目录中添加任何图像攵件并将其名称更改为“ testimage.jpg ”。 在您的视图中创建一个新视图并包含此文件,如 -
你应该得到以下输出 -
我们还可以使用以下程序设置多个靜态资产目录 -
我们还可以提供用于提供静态文件的路径前缀 例如,如果要提供类似'/static'的路径前缀则需要在index.js文件中包含以下代码 -
现在,只偠您需要包含一个文件(例如位于公共目录中的名为main.js的脚本文件),请使用以下脚本标记 -
当将多个目录作为静态文件提供时此技术可鉯派上用场。 这些前缀可以帮助区分多个目录
表格是网络不可分割的一部分。 我们访问的几乎每个网站都为我们提供了提交或获取一些信息的表格 要开始使用表单,我们将首先安装body-parser (用于解析JSON和url编码的数据)和multer(用于解析multipart/form数据)中间件
用以下代码替换index.js文件内容 -
让我们創建一个html表单来测试它。 使用以下代码创建一个名为form.的新视图 -
使用以下命令运行服务器
现在转到localhost:3000 /并填写表单,然后提交 将显示以下回複 -
看看你的控制台; 它会将您的请求正文显示为JavaScript对象,如下面的屏幕截图所示 -
req.body对象包含已解析的请求正文 要使用该对象中的字段,只需像普通的JS对象一样使用它们
这是发送请求的最佳方式。 还有很多其他方法但这些方法与此无关,因为我们的Express应用程序将以相同的方式处悝所有这些请求 要阅读有关提出请求的不同方式的更多信息,请查看页面
我们不断收到请求,但最终没有将它们存储在任何地方 我們需要一个数据库来存储数据。 为此我们将使用名为MongoDB的NoSQL数据库。
要安装和阅读有关Mongo的信息请点击
对于文档建模,我们创建一个Model (非常類似于面向文档的编程中的class )然后我们使用这个Model生成documents (就像我们在OOP中创建documents of a class )。 我们所有的处理都将在这些“文档”上完成最后,我们將在这些文档中编写这些文档
现在您已经安装了Mongo,让我们安装Mongoose就像我们安装其他节点包一样 -
在我们开始使用mongoose之前,我们必须使用Mongo shell创建┅个数据库 要创建新数据库,请打开终端并输入“mongo” 一个Mongo shell将启动,输入以下代码 -
将为您创建一个新数据库 无论何时打开mongo shell,它都将默認为“test”db您必须使用与上面相同的命令更改为数据库。
现在我们的应用程序已连接到我们的数据库让我们创建一个新的模型。 该模型將作为我们数据库中的集合 要创建新模型,请在定义任何路径之前使用以下代码 -
上面的代码定义了一个人的模式用于创建一个Mongoose模式Person 。
現在我们将创建一个新的html表单; 此表单将帮助您获取人员的详细信息并将其保存到我们的数据库中。 要创建表单请在views目录中创建一个名為person.的新视图文件,其中包含以下内容 -
转到“ localhost:3000/person ”以检查表单是否显示正确的输出 请注意,这只是UI它还没有工作。 以下屏幕截图显示了表單的显示方式 -
我们现在将在'/person'定义一个后处理程序处理程序它将处理此请求
在上面的代码中,如果我们收到任何空字段或者没有收到任何芓段我们将发送错误响应。 但是如果我们收到格式良好的文档那么我们从Person模型创建一个newPerson文档,并使用newPerson.save()函数将其保存到我们的DB中 这是茬Mongoose中定义的,并接受回调作为参数 这个回调有2个参数 - 错误和响应。
要显示此路线的响应我们还需要创建一个show_message视图。 使用以下代码创建噺视图 -
我们现在有一个创建persons的界面
Mongoose提供了许多检索文档的功能,我们将重点关注其中的3个 所有这些函数也将回调作为最后一个参数,僦像save函数一样它们的参数是错误和响应。 这三个功能如下 -
此函数查找与条件对象中的字段匹配的所有文档 Mongo中使用的相同运算符也适用於mongoose。 例如
这将从该人的集合中获取所有文档。
这将获取字段名称为“Ayush”且年龄为20的所有文档
我们还可以提供我们需要的投影,即我们需要的字段 例如,如果我们只想要nationality为"Indian"的人的names 我们使用 -
此函数始终提取单个最相关的文档。 它与Model.find()具有完全相同的参数
此函数将_id (由mongo定義)作为第一个参数,一个可选的投影字符串和一个处理响应的回调 例如,
现在让我们创建一条查看所有人员记录的路线 -
Mongoose提供3种更新文檔的功能 功能描述如下 -
此函数采用条件并将对象更新为输入,并将更改应用于与集合中的条件匹配的所有文档 例如,以下代码将更新所有Person文档中的“American”国籍 -
它根据查询找到一个文档并根据第二个参数进行更新。 它还需要一个回调作为最后一个参数 让我们执行以下示唎来理解该函数
此函数更新由其id标识的单个文档。 例如
现在让我们创建一个更新人员的路线。 这将是一个PUT路由其中??id为参数,有效載荷中包含详细信息
要测试此路线,请在终端中输入以下内容(将ID替换为您创建的people的ID) -
这将使用上述详细信息更新与路径中提供的id相关聯的文档
我们已经介绍了Create, Read和Update ,现在我们将看到如何使用Mongoose来Delete文档 我们这里有3个功能,就像更新一样
此函数将条件对象作为输入,并删除与条件匹配的所有文档 例如,如果我们需要删除所有20岁的人请使用以下语法 -
此函数根据条件对象删除single最相关的文档。 让我们执行以丅代码来理解相同的内容
此函数删除由其id标识的单个文档。 例如
现在让我们创建一条从数据库中删除人员的路线。
要检查输出请使鼡以下curl命令 -
这将删除给定id的人产生以下消息 -
Cookie是简单的小文件/数据,通过服务器请求发送到客户端并存储在客户端 每次用户加载网站时,嘟会随请求一起发送此cookie 这有助于我们跟踪用户的操作。
要检查您的cookie是否已设置只需转到浏览器,启动控制台然后输入 -
您将获得类似嘚输出(您可能因浏览器中的扩展而设置更多cookie) -
浏览器每次查询服务器时都会发回cookie。 要从服务器查看cookie请在路由中的服务器控制台上,将鉯下代码添加到该路由
下次向此路由发送请求时,您将收到以下输出
您可以添加过期的Cookie。 要添加过期的cookie只需将属性“expire”的对象设置为您希望它到期的时间。 例如
设置到期时间的另一种方法是使用'maxAge'属性。 使用此属性我们可以提供相对时间而不是绝对時间。 以下是此方法的示例
要删除cookie,请使用clearCookie函数 例如,如果需要清除名为foo的cookie请使用以下代码。
在下一章中我们将了解如何使用cookie来管理会话。
HTTP是无状态的; 为了将请求与任何其他请求相关联您需要一种在HTTP请求之间存储用户数据的方法。 Cookie和URL参数都是在客户端和服务器之間传输数据的合适方式 但它们都是可读的并且在客户端。 Sessions解决了这个问题 您为客户端分配一个ID,并使用该ID进行所有进一步的请求 与愙户端关联的信息存储在链接到此ID的服务器上。
我们需要Express-session 所以使用以下代码安装它。
我们将会话和cookie-parser中间件放在适当的位置 在此示例中,我们将使用默认存储来存储会话即MemoryStore。 切勿在生产环境中使用它 会话中间件为我们处理所有事情,即创建会话设置会话cookie和在req对象中創建会话对象。
每当我们再次从同一客户端发出请求时我们就会将它们的会话信息存储在我们这里(假设服务器没有重新启动)。 我们鈳以向会话对象添加更多属性 在以下示例中,我们将为客户端创建一个视图计数器
上述代码的作用是,当用户访问该站点时它会为該用户创建一个新会话并为其分配一个cookie。 下次用户访问时将检查cookie并相应地更新page_view会话变量。
现在如果您运行应用程序并转到localhost:3000 ,将显示以丅输出
如果您再次访问该页面,页面计数器将会增加 以下屏幕截图中的页面刷新了42次。
身份验证是将提供的凭据与本地操作系统或身份验证服务器中授权用户信息的数据库中的凭据进行比较的过程 如果凭据匹配,则完成该过程并授予用户访问权限
为了我们创建一个身份验证系统,我们需要创建一个注册页面和一个用户密码存储 以下代码为我们创建了一个帐户并将其存储在内存中。 这只是为了演示嘚目的; 建议始终使用持久存储(数据库或文件)来存储用户信息
我们已经为这两个字段设置了必需的属性,因此在我们提供id和密码之前支持HTML5的浏览器不会让我们提交此表单。 如果有人尝试使用没有用户ID或密码的卷曲请求进行注册则会显示错误。 在视图中创建一个名为protected_page.嘚新文件其中包含以下内容 -
只有在用户刚刚注册或登录时才能看到此页面。现在让我们定义其路线以及登录和注销的路线 -
我们创建了一個中间件函数checkSignIn来检查用户是否已登录checkSignIn protected_page使用此函数 要将用户注销,我们会销毁会话
现在让我们创建登录页面。 将视图命名为login.并输入内容 -
峩们的简单认证应用程序现已完成; 现在让我们测试一下应用程序 使用nodemon index.js运行应用程序,然后继续执行localhost:3000/signup
输入用户名和密码,然后单击注册 如果详细信息有效/唯一,您将被重定向到protected_page -
现在退出应用程序 这会将我们重定向到登录页面 -
此路由受到保护,如果未经身份验证的人试圖访问它他将被编辑到我们的登录页面。 这完全是关于基本用户身份验证 始终建议我们使用持久会话系统并使用哈希进行密码传输。 現在有更好的方法来验证用户利用JSON令牌。
RESTful URI和方法为我们提供了处理请求所需的几乎所有信息 下面给出的表总结了如何使用各种动词以忣如何命名URI。 我们将在最后创建一个电影API; 现在让我们讨论一下它的结构
获取所有电影及其详细信息的列表 |
使用提供的详细信息创建新电影。 Response包含此新创建资源的URI |
修改影片ID 1234(如果尚不存在,则创建一个) Response包含此新创建资源的URI。 |
应删除电影ID 1234(如果存在) 响应应包含请求嘚状态。 |
应该是无效的 DELETE和PUT应指定他们正在处理的资源。 |
现在让我们在Express中创建此API 我们将使用JSON作为我们的传输数据格式,因为它易于在JavaScript中使用并具有其他好处 将movies.js文件替换为movies.js文件,如以下程序中所示
现在我们已经设置了应用程序,让我们专注于创建API
首先设置movies.js文件。 我们沒有使用数据库来存储电影而是将它们存储在内存中; 所以每次服务器重启时,我们添加的电影都会消失 这可以使用数据库或文件(使鼡节点fs模块)轻松模仿。
让我们定义获取所有电影的GET路线 -
要测试这是否正常运行您的应用程序,然后打开您的终端并输入 -
我们有一条获嘚所有电影的路线 现在让我们创建一个通过其id获取特定电影的路线。
这将根据我们提供的ID为我们提供电影 要检查输出,请在终端中使鼡以下命令 -
如果您访问无效路由则会产生cannot GET error而如果您访问ID不存在的有效路由,则会产生404错误
我们完成了GET路由,让我们现在转到POST路由
使鼡以下路由处理POSTed数据 -
这将创建一个新电影并将其存储在电影变量中。 要检查此路线请在终端中输入以下代码 -
要测试是否已将其添加到movies对潒,请再次运行/movies/105的get请求 将显示以下回复 -
让我们继续创建PUT和DELETE路由。
PUT路由与POST路由几乎相同 我们将指定要更新/创建的对象的id。 按以下方式创建路径
此路由将执行上表中指定的功能。 它将使用新的详细信息更新对象(如果存在) 如果它不存在,它将创建一个新对象 要检查蕗由,请使用以下curl命令 这将更新现有的电影。 要创建新电影只需将ID更改为不存在的ID。
使用以下代码创建删除路由 -
检查路线的方式与檢查其他路线的方式相同。 删除成功后(例如id 105)您将获得以下输出 -
最后,我们的movies.js文件将如下所示
这样就完成了我们的REST API。 现在您可以使用这种简单的架构风格和Express创建更复杂的应用程序。
脚手架允许我们轻松地skeleton for a web application创建skeleton for a web application 我们手动创建公共目录,添加中间件创建单独的路径攵件等。脚手架工具为我们设置所有这些东西以便我们可以直接开始构建我们的应用程序。
我们将使用的脚手架叫做Yeoman 它是为Node.js构建的脚掱架工具,但也有几个其他框架的生成器(如flaskrails,django等) 要安装Yeoman,请在终端中输入以下命令 -
Yeoman使用生成器来构建应用程序 要查看npm上可用的苼成器与Yeoman一起使用,您可以单击此 在本教程中,我们将使用'generator-Express-simple' 要安装此生成器,请在终端中输入以下命令 -
要使用此生成器请输入以下命令 -
系统会向您询问一些简单的问题,例如您希望在应用中使用哪些内容 选择以下答案,或者如果您已经了解这些技术那么请选择您唏望的方式。
然后它将为您创建一个新的应用程序安装所有依赖项,向您的应用程序添加几个页面(主页404找不到页面等)并为您提供┅个目录结构。
这台发电机为我们创造了一个非常简单的结构 探索可用于Express的众多发电机并选择适合您的发电机。 使用所有发电机的步骤昰相同的?? 您需要安装一台发电机,使用Yeoman运行它; 它会问你一些问题然后根据你的答案为你的应用程序创建一个框架。
Express中的错误处理昰使用中间件完成的 但是这个中间件具有特殊属性。 错误处理中间件的定义方式与其他中间件函数的定义相同只是错误处理函数MUST have four arguments而不昰三个 - err, req, res, next 。 例如要发送任何错误的响应,我们可以使用 -
直到现在我们正在处理路线本身的错误 错误处理中间件允许我们分离错误逻辑并楿应地发送响应。 我们在中间件中讨论的next()方法将我们带到下一个middleware/route handler
对于错误处理,我们有next(err)函数 对此函数的调用会跳过所有中间件并將我们匹配到该路由的下一个错误处理程序。 让我们通过一个例子来理解这一点
可以在路由或包含条件之后策略性地放置此错误处理中間件以检测错误类型并相应地响应客户端。 以上程序将显示以下输出
Express使用模块在内部记录有关路由匹配,中间件功能应用程序模式等嘚信息。
要查看Express中使用的所有内部日志请在启动应用程序时将DEBUG环境变量设置为Express:* -
当应用程序的某个组件无法正常运行时,这些日志非常有鼡 这个详细的输出可能有点压倒性。 您还可以将DEBUG变量限制为要记录的特定区域 例如,如果您希望将记录器限制为应用程序和路由器則可以使用以下代码。
默认情况下调试处于关闭状态,并在生产环境中自动打开 调试也可以扩展以满足您的需求,您可以在阅读更多楿关信息
与具有定义的处理方式文件结构等的Django和Rails不同,Express不遵循定义的方式 这意味着您可以按照自己喜欢的方式构建应用程序。 但是随著应用程序的大小增加如果它没有明确定义的结构,则很难维护它 在本章中,我们将介绍常用的目录结构和关注点分离以构建我们嘚应用程序。
首先我们将讨论创建节点和Express应用程序的最佳实践。
始终使用npm init开始一个节点项目
始终使用--save或--save-dev安装依赖项。 这将确保如果您迻动到其他平台则可以运行npm install来安装所有依赖项。
坚持使用小写文件名和camelCase变量 如果查看任何npm模块,它以小写字母命名并用短划线分隔 烸当您需要这些模块时,请使用camelCase
不要将node_modules推送到您的存储库。 相反npm会在开发机器上安装所有内容。
使用config文件存储变量
将路由分组并隔离箌自己的文件 例如,在我们在REST API页面中看到的电影示例中进行CRUD操作
现在让我们讨论Express的目录结构。
Express没有用于创建应用程序的社区定义结构 以下是网站的主要项目结构。
还有其他方法可以使用Express构建网站 您可以使用MVC设计模式构建网站。 有关更多信息请访问以下链接。
您还鈳以使用自动来获得类似的结构
本章列出了我们在本教程中使用的各种资源。
快递网站上提供的不同方面的指南也很有帮助 -
有关Express的有用書籍和博客列表请访问
有关Express的大多数中间件列表,请访问
这些带有Express提示和技巧的博客可能会有所帮助 -
为什么要引入有什么特别の处呢?有一些嵌套层次较深的页面可能会出现巢状嵌套,如下图所示
在后期维护和修改时一不小心少了一个尖括号,或者某个標签的开始和闭合没有对应上就会导致DOM结构的混乱甚至是错误。所以有人发明了HAML,它最大的特色就是使用缩进排列来代替成对标签受HAML的启发,进行了javascript的实现
原名不叫,是大名鼎鼎的jade后来由于商标的原因,改为哈巴狗。其实只是换个名字语法都与jade一样。丑話说在前面有它本身的缺点——可移植性差,调试困难性能并不出色,但使用它可以加快开发效率本文将详细介绍模板引擎
但運行命令时,提示命令未找到
这时需要安装命令行工具-cli
[注意]一定要全局安装-cli,否则无法正常编译
再运行命令时正常执行
在学习基础语法之前,首先要了解的命令行的使用
将如下内容输入文件中并命名为') 百度
如果有很多属性,可以把它们分几荇写
如果有一个很长的属性并且JavaScript运行时引擎支持ES2015模板字符串,可以使用它来写属性值
如果属性名称中含有某些奇怪的字符可能会与 JavaScript 语法产生冲突的话,可以将它们使用 ""
或者 ''
括起来还可以使用逗号来分割不同的属性
默认情况下,所有的属性都经过转义(即紦特殊字符转换成转义序列)来防止诸如跨站脚本攻击之类的攻击方式如果要使用特殊符号,需要使用 !=
而不是 =
[注意]未经转义的缓存玳码十分危险必须正确处理和过滤用户的输入来避免跨站脚本攻击
在中,布尔值属性是经过映射的这样布尔值(true
和false)
就能接受了。没囿指定值时默认是true
style
(样式)属性可以是一个字符串(就像其他普通的属性一样)还可以是一个对象
考虑到使用 div
作为标签名这种荇为实在是太常见了,所以如果省略掉标签名称的话它就是默认值
如果JavaScript运行时支持 ECMAScript 2015 模板字符串,还可以使用下列方式来简化属性值
&attributes
语法可以将一个对象转化为一个元素的属性列表
变量来源有三种分别是文件内部、命令行参数和外部JSON文件
1、文件内部
使用--obj参数,就可以跟随一个对象形式的参数
3、外部JSON文件
使用-O跟随一个JSON文件的路径即可
这三种方式,文件内部的变量优先级朂多而外部JSON文件和命令行传参优先级相同
如下所示,外部JSON文件和命令行传参两种方式都存在由于--obj写在-w后面,最终以命令行传参为准
的条件判断的一般形式的括号是可选的所以可以省略掉开头的 -
,效果完全相同类似一个常规的 JavaScript 语法形式
同样也提供了它的反义版本 unless
在某些情况下,如果不想输出任何东西的话可以明确地加上一个原生的 break
语句
也可以使用块展开的语法
目前支持两種主要的迭代方式: each
和 while
这是 的首选迭代方式
可以在迭代时获得索引值
能够迭代对象中的键值
用于迭代的对象或数组仅仅昰个 JavaScript 表达式,因此它可以是变量、函数调用的结果又或者其他
还能添加一个 else
块,这个语句块将会在数组与对象没有可供迭代的值时被执行
[注意]也可以使用 for
作为 each
的别称
也可以使用 while
来创建一个循环
混入是一种允许在 中重复使用一整个代码块的方法
混入可鉯被编译成函数形式并传递一些参数
混入也可以把一整个代码块像内容一样传递进来
混入也可以隐式地從“标签属性”得到一个参数 attributes
[注意]+link(class="btn")
等价于 +link()(class="btn")
,因为 会判断括号内的内容是属性还是参数但最好使用后面的写法,明确地传递空的参数确保第一对括号内始终都是参数列表
可以用剩余参数(rest arguments)语法来表示参数列表最后传入若干个长度不定的参数
包含(include)功能允許把另外的文件内容插入进来
被包含的如果不是 文件那么就只会当作文本内容来引入
支持使用 block
和 extends
关键字进行模板的继承一个称之为“块”(block)的代码块,可以被子模板覆盖、替换这个过程是递归的。
的块可以提供一份默认内容当然这是可选的
现在来扩展这个布局:只需要简单地创建一个新文件,并如丅所示用一句 extends
来指出这个被继承的模板的路径现在可以定义若干个新的块来覆盖父模板里对应的“父块”。值得注意的是因为这里的 foot
塊 没有 被重定义,所以会依然输出“一些页脚的内容”
同样也可以覆盖一个块并在其中提供一些新的块。如下所示content
块现在暴露出兩个新的块 sidebar
和 primary
用来被扩展。当然它的子模板也可以把整个 content
给覆盖掉
允许去替换(默认的行为)、prepend
(向头部添加内容),或者 append
(向尾蔀添加内容)一个块 假设有一份默认的脚本要放在 head
块中,而且希望将它应用到 每一个页面可以进行如下操作
现在假设有一个页面,那是一个 JavaScript 编写的游戏希望把一些游戏相关的脚本也像默认的那些脚本一样放进去,那么只要简单地 append
这个块:
版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。