Express是Node.js的最佳框架之一。它具有大力的支持和许多有用的功能。那里有很多很棒的文章,涵盖了所有基础知识。但是,这次我想深入研究,并分享我的工作流程以创建一个完整的网站。通常,本文不仅用于Express,而且还将其与其他一些可用于节点开发人员使用的优质工具结合使用。
要遵循本教程,我假设您对节点有些熟悉,并且已将其安装在系统上。
Express的核心是连接。这是一个中间件框架,配备了许多有用的东西。如果您想知道什么是中间件,这是一个快速示例:
const connect = require('connect'),<br> http = require('http');<br><br> const app = connect()<br> .use(function(req,res,ext)<br> console.log(“那是我的第一个中间件”);<br> 下一个();<br> }))<br> .use(function(req,res,ext)<br> console.log(“那是我的第二个中间件”);<br> 下一个();<br> }))<br> .use(function(req,res,ext)<br> console.log(“ end”);<br> res.end(“ Hello World”);<br> });<br><br> http.Createserver(App).Listen(3000);<br>
中间件基本上是一个函数,该函数接受响应对象和响应对象,或通过在第二个中间件中调用Next()方法来调用下一个函数,Body Parser解析请求主体并支持应用程序/JSON,Application/X-WWW-Form-urlCormeded,以及Multipart/form-data。并用cookie名称键入的对象进行req.cookies。
Express Craps实际上连接并添加了一些新功能,例如路由逻辑,这使得该过程更加顺畅。这是处理Express中的GET请求的示例:
app.get('/hello.txt',function(req,res){<br> var body ='Hello world';<br> res.setheader('content-type','text/plain');<br> res.setheader(“内容长度”,body.mength);<br> res.end(身体);<br> });<br>
我们构建的示例网站的源代码可在GitHub上使用。请随意分叉并与之一起玩。这是运行网站的步骤。
{<br> “名称”:“ mywebsite”,<br> “描述”:“我的网站”,<br> “版本”:“ 0.0.1”,<br> “依赖关系”:{<br> “ express”:“ 5.x”<br> }<br> }<br>
框架的代码将放置在node_modules中,您将能够创建一个实例。但是,我更喜欢使用命令行工具一个替代选项。通过使用NPX Express-Generator命令:
用法:Express [选项] [DIR]<br><br> 选项:<br><br> - version输出版本号<br> -e,-ej添加EJS引擎支持<br> - Pug添加哈哈发动机支撑<br> -HBS添加车把引擎支持<br> -h, - 霍根添加hogan.js引擎支持<br> -v,-view <engine>添加视图<engine>支持(灰尘|<br> - 不使用静态HTML代替视图引擎<br> -c,-css <egene>添加样式表<engine> support> support> sandlus | Compass | sass)(默认为普通CSS)<br> - git添加.gitignore<br> -F, - 非空目录的力量<br> -h, - 螺旋输出使用信息<br></engine></egene></engine></engine>
如您所见,只有一些可用的选择,但是对我来说,它们已经足够了。通常,我少用作为CSS预处理器和车把作为模板引擎。在此示例中,我们还需要会话支持,因此NPM安装和NODE_MODULES文件夹将弹出。
我意识到上述方法并不总是合适的。您可能需要将路线处理程序放置在另一个目录或类似目录中。但是,正如您将在接下来的几节中看到的那样,我将对已经生成的结构进行更改,并且很容易做到。因此,您应该只是想到app.js来使用我们的新文件结构。我们需要删除这两行:
const usersrouter = require(“ ./路由/用户”);<br> ...<br> app.use(“/用户”,usersrouter);<br>
现在,我们需要设置配置。让我们想象我们的小网站应部署到三个不同的位置:本地服务器,登台服务器和生产服务器。当然,每个环境的设置都不同,我们应该实施一种足够灵活的机制。如您所知,每个节点脚本都是作为控制台程序运行的。因此,我们可以轻松地发送将定义当前环境的命令行参数。我将该部分包裹在单独的模块中,以便稍后为其编写测试。这是/config/index.js文件:
const config = {<br> 当地的: {<br> 模式:“本地”,<br> 端口:3000<br> },,<br> 登台:{<br> 模式:“分期”,<br> 端口:4000<br> },,<br> 生产: {<br> 模式:“生产”,<br> 端口:5000<br> }<br> }<br> Module.exports = function(mode){<br> 返回配置[模式|| process.argv [2] || 'local'] || config.local;<br> }<br>
(现在)只有两个设置:端口。您可能已经猜到了,该应用程序为不同的服务器使用不同的端口。这就是为什么我们必须在app.js中更新站点的输入点的原因。
const config = require('./ config')();<br> process.env.port = config.port;<br>
要在配置之间切换,只需在末尾添加环境即可。例如:
NPM开始登台<br>
将在端口4000运行服务器。
现在,我们将所有设置都放在一个地方,并且很容易管理。
我是测试驱动开发(TDD)的忠实拥护者。我将尝试介绍本文中使用的所有基础类。当然,对所有内容进行测试都会使这本书的写作太长,但是总的来说,这就是创建自己的应用程序时应该继续进行的方式。我最喜欢的测试框架之一是UVU,因为它非常易于使用和快速使用。当然,它可以在NPM注册表中使用:
npm安装 - save-dev uvu<br>
然后,在NPM测试中创建一个新脚本,您应该看到以下内容:
config.js<br> •••(3/3)<br><br> 总计:3<br> 通过:3<br> 跳过:0<br> 持续时间:0.81ms<br>
这次,我首先编写了实施,第二个测试。这并不是TDD做事的方式,而是在接下来的几个部分中,我将相反。
我强烈建议您花费大量时间写作测试。没有什么比全面测试的应用程序更好的了。
几年前,我意识到一些非常重要的事情,这可能有助于您制作更好的程序。每次您开始编写新课程,新模块或仅仅是新逻辑时,请问自己:
我该如何测试?
这个问题的答案将有助于您更有效地编码,创建更好的API,并将所有内容放入良好的块中。您不能为意大利面代码编写测试。例如,在上面的配置文件( /config/index.js )中,我添加了发送生产配置的可能性,但是节点脚本是使用NPM安装MongoDB运行的。
接下来,我们将编写一个测试,该测试检查是否运行了MongoDB服务器。这是/tests/mongodb.js文件:
const {test} = require(“ uvu”);<br> const {mongoclient} = require(“ mongodb”);<br><br> test(“ mongodb服务器活动”,async函数(){<br> const client = new mongoclient(“ mongodb://127.0.0.1:27017/fastdelivery”);<br> 等待client.connect();<br> });<br><br> test.run();<br><br>
我们不需要添加MongoDB客户端的任何连接方法,每当我们必须向数据库提出请求时,都会接收一个mongoclient对象。因此,我们应该连接到初始服务器创建中的数据库。为此,由于中间件在每个请求之前自动运行,因此在Req.db属性中可用。
我们都知道MVC模式。问题是如何适用表达。或多或少,这是解释的问题。在接下来的几个步骤中,我将创建模块,该模块充当模型,视图和控制器。
该模型将是处理我们应用程序中的数据的方法。它应该可以使用杂种。我们的模型还应该有一种扩展它的方法,因为我们可能需要创建不同类型的模型。例如,我们可能想要一个contactsmodel。因此,我们需要编写一个新规格, /tests /base.model.js ,以测试这两个模型功能。并记住,通过在开始编码实现之前定义这些功能,我们可以保证我们的模块只能完成我们想要的工作。
const {test} = require(“ uvu”);<br> const sustert = require(“ uvu/servert”);<br> const ModelClass = require(“ ../ models/base”);<br> const dbmockup = {};<br> test(“模块创建”,异步函数(){<br> const Model =新的ModelClass(DBMOCKUP);<br> assert.ok(model.db);<br> assert.ok(model.setdb);<br> assert.ok(model.Collection);<br> });<br> test.run();<br>
而不是真实的DB对象和我们的数据库视图目录的Getter将更改为基本视图类。现在,这个小的更改需要另一个更改。我们应该通知明确说明我们的模板文件现在放置在另一个目录中:
app.set(“ views”,path.join(__ dirname,“模板”));<br>
首先,我将定义我需要的内容,编写测试,然后编写实现。我们需要一个匹配以下规则的模块:
const data = {开发人员:“ krasimir tsonev”};<br> response.conttype(“ application/json”);<br>响应send(json.stringify(data));<br>
拥有JSONVIEW类,甚至是测试目录,而不是每次都这样做,如果您在路线之后运行'/'(在上面的示例中,实际上是控制器)是一个接受响应的函数,并且是一个接受响应的函数,并且Express(1)命令行工具将是一个名为“旧界限”功能,它是旧的Middledware函数,它是旧的Middledware函数,它是一个旧的Middledware函数,它是optres ofer
好的,我们为MVC体系结构提供了一系列的课程,并且我们已经通过测试介绍了新创建的模块。现在,我们准备继续使用假公司FastDelivery的网站。
让我们想象该站点有两个部分:前端和管理面板。前端将用于向我们的最终用户显示数据库中写的信息。管理面板将用于管理该数据。让我们从管理员(控制)面板开始。
让我们首先创建一个简单的控制器,该控制器将用作管理页面。这是/routes/admin.js文件:
const basecontroller = require(“ ./ base”),<br> view = require(“ ../ view/base”);<br> Module.exports = new(class adminController扩展了basecontroller {<br> constructor(){<br> 超级(“ admin”);<br> }<br> 运行(req,res,next){<br> if(this.authorize(req)){<br> req.session.fastDelivery = true;<br> req.session.save(function(err){<br> var v =新视图(res,“ admin”);<br> v.render({<br> 标题:“管理”,<br> 内容:“欢迎来到控制面板”,<br> });<br> });<br> } 别的 {<br> const v =新视图(res,“ admin-login”);<br> v.render({<br> 标题:“请登录”,<br> });<br> }<br> }<br> 授权(req){<br> 返回 (<br> (Req.Session &&<br> req.session.fastDelivery &&<br> req.session.fastDelivery === true)||<br> (req.body &&<br> req.body.username === this.username && &&<br> req.body.password === this.password)<br> );<br> }<br> })();<br>
通过为控制器和视图使用预编写的基本类,我们可以轻松地为控制面板创建入口点。 admin.run方法直接作为中间件。那是因为我们想保留上下文。如果我们这样做:
app.all('/admin*',admin.run);<br>
admin一词将指向其他内容。
以/管理为开头的每个页面都应受到保护。为了实现这一目标,我们将使用Express的中间件:Session。它只需将对象附加到称为admin Controller的请求即可做两个其他事情:
这是我们可以用来实现这一目标的小辅助功能:
授权(req){<br> 返回 (<br> (Req.Session &&<br> req.session.fastDelivery &&<br> req.session.fastDelivery === true)||<br> (req.body &&<br> req.body.username === this.username && &&<br> req.body.password === this.password)<br> );<br> }<br>
首先,我们有一个声明,试图通过会话对象识别用户。其次,我们检查是否已提交表格。如果是这样,则来自表单的数据在BodyParser中间件中可用。然后,我们只检查用户名和密码是否匹配。
现在,这是标题,图片和类型属性将确定记录的所有者。例如,“联系人”页面只需要一个使用Admin Controller的记录。为了简化任务,我决定将添加记录列表和添加/编辑的表格组合起来。如您在下面的屏幕截图中看到的那样,该页面的左侧为列表保留,并保留该表单的右侧部分。
在一个页面上拥有所有内容意味着我们必须集中精力在呈现页面的部分,或更具体地说,是我们发送到模板的数据。这就是为什么我创建了几个合并的助手功能,例如:
this.del(req,function(){<br> this.form(req,res,function(formmarkup){<br> this.list(function(listmarkup){<br> v.render({<br> 标题:“管理”,<br> 内容:“欢迎来到控制面板”,<br> 列表:Listmarkup,<br> 表格:formmarkup,<br> });<br> });<br> });<br> });<br> const v =新视图(res,“ admin”);<br>
它看起来有些丑陋,但它可以按照我的意愿起作用。第一个助手是一个操作= delete&id = [记录的ID],它从集合中删除了数据。第二个功能称为列表方法获取信息并准备HTML表,后来将其发送到模板。可以在本教程的源代码中找到这三个帮助者的实现。
在这里,我决定向您展示处理admin.js中的文件上传的功能:
handerfileupload(req){<br> 如果(!<br> 返回req.body.currentPicture || “”<br> }<br> const data = fs.ReadFileSync(req.files.picture.path);<br> const filename = req.files.picture.name;<br> const uid = crypto.randombytes(10).tostring(“ hex”);<br> const dir = __dirname“ /../public/uploads/” uid;<br> fs.mkdirsync(dir,“ 0777”);<br> fs.WriteFileSync(dir“/”文件名,数据);<br> 返回“/uploads/“ uid”/“ fileName;<br> }<br>
如果提交文件,则节点脚本req.files.picture。在上面的代码段中,readfilesync,writefilesync。
艰苦的工作现在已经完成。管理面板正在工作,我们有一个家庭和四个记录,其中包含博客的DB对象,但请致电“不同 /blog /:id string”。该路线将匹配req.params.id等URL。换句话说,我们能够定义动态参数。在我们的情况下,这就是记录的ID。获得此信息后,我们可以为每篇文章创建一个唯一的页面。
第二个有趣的部分是我如何构建服务,职业和联系人页面。显然,他们仅使用数据库中的一个记录。如果我们必须为每个页面创建一个不同的控制器,那么我们必须复制/粘贴相同的代码,只需在其NPM安装命令中更改类型即可运行以安装新的依赖项(如果有)。
请记住,节点仍然还很年轻,因此并非所有内容都可以按照您的预期工作,但是一直都有改进。例如,永远保证您的node.js程序将连续运行。您可以通过发出以下命令来执行此操作:
永远启动您的app.js<br>
这也是我在服务器上使用的内容。这是一个不错的小工具,但它解决了一个大问题。如果您使用永远的应用程序运行,请简单地重新启动应用程序。
现在我不是系统管理员,但是我想分享我的经验将节点应用与Apache或Nginx集成,因为我认为这是开发工作流程的一部分。
如您所知,Apache通常在端口80上运行,这意味着,如果您打开http:// localhost:80,您将看到Apache Server提供的页面,并且很可能您的节点脚本在其他端口上收听。因此,您需要添加一个接受请求并将其发送到正确端口的虚拟主机。例如,假设我想托管我们刚刚在主机文件下的本地Apache服务器上构建的网站。
127.0.0.1 ExpressCompletewebsite.dev<br>
之后,我们必须在Apache Configuration目录下编辑HTTPD-VHOSTS.CONF文件,然后添加:
#ExpressCompleteWebsite.dev<br> <virtualhost><br> Servername ExprexPleteWebsite.dev<br> Serveralias www.expresscompletewebsite.dev<br> proxyrequest off<br> <br> 订单拒绝,允许<br> 从所有人那里允许<br> <br> <br> proxypass http:// localhost:3000/<br> ProxypassReverse http:// localhost:3000/<br> <br> </virtualhost><br>
服务器仍然接受端口80上的请求,但将其转发到端口3000,该端口正在侦听。
NGINX设置要容易得多,而且,老实说,它是托管基于node.js的应用程序的更好选择。您仍然必须在主机文件中添加域名。之后,只需在NGINX安装下的/启用 /站点的目录中创建一个新文件即可。文件的内容看起来像这样:
服务器 {<br> 听80;<br> server_name ExpressCompletewebsite.dev<br> 地点 / {<br> Proxy_pass http://127.0.0.1:3000;<br> proxy_set_header主机$ http_host;<br> }<br> }<br>
请记住,您不能使用上述主机设置同时运行Apache和Nginx。那是因为它们都需要端口80。此外,如果您计划在生产环境中使用上述代码段,则可能需要对更好的服务器配置进行一些其他研究。正如我所说,我不是这个领域的专家。
Express是一个很棒的框架,它为您提供了开始构建应用程序的良好起点。如您所见,这是您将如何扩展它以及与之构建的方法的选择。它通过使用一些出色的中间件来简化无聊的任务,并将有趣的零件留给开发人员。
雅各布·杰克逊(Jacob Jackson)的贡献已更新。雅各布(Jacob)是网络开发人员,技术作家,自由职业者和开源贡献者。
以上是使用Express建立一个完整的MVC网站的详细内容。更多信息请关注PHP中文网其他相关文章!