找回密码
 立即注册
首页 python Python-Web 查看内容
在这篇第一篇文章中,我们回答了“什么是Node?”以及“什么是Express?”,让您大致了解一下什么使Express web框架变得特别。我们将概述主要特性,并向您展示一个Express应用程序的主要构建块(尽管目前还没有一个用于测试的开发环境)。


先决条件:
基本的计算机知识。对服务器端网站编程的一般理解,特别是在网站上的客户机-服务器交互的机制
学习目标:
要熟悉Express是什么,以及它是如何与节点相适应的,它提供了什么功能,以及Express应用程序的主要构建块。



Express and Node 是什么?


Node(或更正式的Node.js)是一个开源的、跨平台的、运行时环境,允许开发人员在JavaScript中创建各种服务器端工具和应用程序。运行时的目的是在浏览器环境之外使用(即直接在计算机或服务器操作系统上运行)。因此,环境会省略特定于浏览器的JavaScript api,并支持更多传统的操作系统api,包括HTTP和文件系统库。


从web服务器开发的角度来看,Node有很多好处:


  • 卓越的性能表现! Node的设计目的是优化web应用程序的吞吐量和可伸缩性,并且非常适合许多常见的web开发问题(例如,实时web应用程序)。
  • 代码是用“普通的旧JavaScript”编写的,这意味着在编写浏览器和web服务器代码时,处理语言之间的“上下文转换”的时间更少。
  • JavaScript是一种相对较新的编程语言,与其他传统的web服务器语言(例如Python、PHP等)相比,语言设计的改进带来了好处。许多其他的新流行的语言编译/转换成JavaScript,所以你也可以使用CoffeeScript, ClosureScript、Scala、LiveScript等等。
  • node包管理器(NPM)提供了对数十万个可重用包的访问。它还具有最佳的类依赖性解决方案,并且可以用于自动化大多数构建工具链.
  • 它是可移植的,版本运行在Microsoft Windows、OS X、Linux、Solaris、FreeBSD、OpenBSD、WebOS和NonStop OS上。此外,它还受到许多web主机提供商的支持,它们通常为托管节点站点提供特定的基础设施和文档。
  • 它有一个非常活跃的第三方生态系统和开发者社区,有很多人愿意提供帮助。



您可以简单地创建一个简单的web服务器。


Hello Node.js


下面的例子创建了一个web服务器,用来监听来自http://127.0.0.1:8000/的所有请求。当接收到一个请求,脚本将返回一个纯文本响应“Hello World”。如果你已经安装了node,你可以按照下面的步骤实验示例:


打开控制台(在Windows系统下,使用命令行工具)


创建一个空文件夹存放项目,如“test-node”,然后在控制台输入以下命令进入这个目录:
cd test-node

使用你的文本编辑器,创建一个名为“hello.js"的文件,将以下的代码粘贴在这个文件中。
// Load HTTP module
var http = require("http");

// Create HTTP server and listen on port 8000 for requests
http.createServer(function(request, response) {

   // Set the response HTTP header with HTTP status and Content type
   response.writeHead(200, {'Content-Type': 'text/plain'});
   
   // Send the response body "Hello World"
   response.end('Hello World\n');
}).listen(8000);

// Print URL for accessing server
console.log('Server running at http://127.0.0.1:8000/');
保存以上创建的文件。


返回控制台并输入下面的命令:
node "hello.js"
最后,在浏览器中导航至"http://localhost:8000";你应当看到“Hello World" 出现在一个空页面的左上部分。


Web开发框架


其他常见的web开发任务不受node本身的直接支持。如果你想添加特定的处理不同的HTTP动词(如GET、POST、DELETE等),分别在不同的URL路径处理请求(“路线”),提供静态文件,或使用模板来动态创建响应,那么您将需要编写自己的代码,或者你也可以避免重新发明轮子和使用web框架!


Express是最流行的node web框架,它是许多其他流行的节点web框架的底层库。它提供了机制:


  • 在不同的URL路径(路由)中使用不同HTTP动词的请求编写处理程序。
  • 与“视图”呈现引擎集成,以便通过将数据插入模板来生成响应。
  • 设置常见的web应用程序设置,比如用于连接的端口,以及用于呈现响应的模板的位置。
  • 在请求处理管道的任何位置添加额外的请求处理“中间件”。



虽然Express本身是非常简单的,但是开发人员已经创建了兼容的中间件包来解决几乎所有的web开发问题。有一些库可以使用cookie、会话、用户登录、URL参数、POST数据、安全标头等等。您可以在Express中间件中找到由Express团队维护的中间件软件包列表(以及一些流行的第三方软件包列表)。


Note: 这种灵活性是一把双刃剑。有一些中间件包可以解决几乎所有的问题或需求,但是使用合适的包有时是一种挑战。在构建应用程序的过程中也没有“正确的方法”,而且您在Internet上可能发现的许多示例不是最优的,或者只是为了开发web应用程序,只显示您需要做的一小部分。


它从哪儿来的?


Node最初是在2009年发布的,只针对Linux。NPM包管理器于2010年发布,并在2012年添加了本地窗口支持。当前的LTS版本是Nodev6.11.11,而最新版本是Node8。这是一个丰富历史的小快照;如果你想知道更多的话,可以深入维基百科。


Express最初于2010年11月发布,目前在API的版本4中。您可以通过签出变更来了解当前版本中的变更信息,以及GitHub提供更详细的历史发行记录。


Node/Express 有多流行?


web框架的流行非常重要,因为它是一个指示器,说明它是否会继续维护,以及在文档、附加库和技术支持方面可能有哪些资源可用。


对于服务器端框架的流行程度,目前还没有任何准备和明确的度量(尽管像热框架这样的网站试图通过计算每个平台的GitHub项目数量和StackOverflow问题等机制来评估受欢迎程度)。一个更好的问题是,Node和Express是否“足够流行”,以避免不受欢迎的平台的问题。它们还在继续进化吗?如果你需要帮助,你能得到帮助吗?如果你学习Express,你有机会获得报酬吗?


基于使用Express的高知名度公司的数量,为代码库做出贡献的人数,以及提供免费和付费支持的人员数量,是的,Express是一个流行的框架!


Is Express opinionated?


Web框架通常将自己称为“固执己见”或“不固执己见”。


固执己见的框架是那些对处理任何特定任务的“正确方法”有意见的人。它们通常支持特定领域的快速开发(解决特定类型的问题),因为正确的方法通常是很好的理解和记录的。然而,在解决主要领域之外的问题时,它们可能不那么灵活,而且对于可以使用的组件和方法,它们提供的选择也更少。


相比之下,那些不固执己见的框架,对于将组件粘合在一起以实现目标的最佳方式,甚至应该使用什么组件的限制,都要少得多。它们使开发人员更容易使用最合适的工具来完成特定的任务,尽管成本是您自己需要找到这些组件的成本。


Express是不固执己见的。您几乎可以将任何您喜欢的任何兼容的中间件插入到请求处理链中。您可以在一个文件或多个文件中构造该应用程序,并使用任何目录结构。有时候你会觉得自己有太多的选择!


Express代码看起来的样子?


在传统的数据驱动网站,一个web应用是等待来自浏览器或其它客户端的HTTP请求的。当web应用收到一个请求,它会根据URL中包含的参数信息,以及POST方式发过来的数据,来解析请求需要的功能。这个取决于根据请求从服务器数据库读取到的信息。web应用返回给浏览器一个由HTML模板的占位符上插入数据动态生成的HTML页面。


Express提供了一些方法来指定特定的HTTP动作(GET, POST, SET等)和URL模式(“路由”)所调用的函数,以及指定使用何种模板(“view”)引擎、模板文件所在位置以及使用哪个模板用来渲染响应(返回内容)的方法。您可以使用Express中间件来添加对cookie、会话和用户,获得POST/GET参数等等的支持。您可以使用Node支持的任何数据库机制(Express没有定义任何与数据库相关的行为)。


下面的部分将解释在书写Express和Node代码时您将遇到的一些常见问题。


Helloworld Express


首先让我们思考一下标准的 ExpressHello World的示例(我们将在下面讨论它的各个部分)。


点子: 如果您已经安装了NodeExpress(或者您按照下一篇文章中的说明去安装它们),可以将此代码保存在名为app.js的文件中,并通过在bash命令提示符中调用以下命令运行它
var express = require('express');
var app = express();

app.get('/', function(req, res) {
  res.send('Hello World!');
});

app.listen(3000, function() {
  console.log('Example app listening on port 3000!');
});
前两行(导入)express模块,并创建 Express 应用程序。这个对象,传统上被命名为 app,具有路由HTTP请求、配置中间件、渲染HTML视图、注册模板引擎以及修改应用程序设置的方法,这些设置控制应用程序的行为(例如,环境模式,路由定义是否为区分大小写等)。


代码的中间部分(从app.get()开始的三行)显示了路由定义。app.get()方法指定了一个回调函数,每当有一个HTTP GET请求具有一个相对于站点根目录的路径('/')时,该回调函数都会被调用。回调函数接受一个请求和一个响应对象作为参数,并简单地调用响应中的send()来返回字符串“Hello World!”


最后一个代码块在端口“3000”上启动服务器,并将日志注释打印到控制台。在服务器运行时,您可以在浏览器中访问localhost:3000,以查看返回的响应示例。


导入和创建模块


模块是一个JavaScript库/文件,您可以使用Node的 require() 函数将其导入到其他代码中。Express本身是一个模块,我们在Express应用程序中使用的中间件和数据库库也是如此。


下面的代码展示了我们如何通过模块名称来导入模块,用Express框架作为示例。首先,我们调用require()函数,用字符串('express')指定模块的名称,并调用返回的对象来创建 Express 应用程序 。然后我们就可以访问应用程序对象的属性和函数。
var express = require('express');
var app = express();
您还可以创建自己的模块,这些模块可以以相同的方式导入。


Tip: 您将希望创建自己的模块,因为这允许您将代码组织成可管理的部分——一个单一的单文件应用程序很难理解和维护。使用模块还可以帮助您管理命名空间,因为只有当您使用模块时,您显式导出的变量才会导入。


要在模块之外提供对象,您只需将它们分配给exports对象即可。例如 下面的 square.js 模块是一个导出了area()perimeter()方法的文件:
exports.area = function(width) { return width * width; };
exports.perimeter = function(width) { return 4 * width; };

我们可以使用 require() 导入这个模块,然后调用导出的方法,如下所示:
var square = require('./square'); // Here we require() the name of the file without the (optional) .js file extension
console.log('The area of a square with a width of 4 is ' + square.area(4));

Note: 您还可以为模块指定一条绝对路径(或名称,就像我们最初所做的那样)。


如果您想一次性导出一个完整的对象,而不是每次只构建一个属性,那么将它像下面这样赋值给module.exports。(您也可以这样做,使导出对象的根为构造函数或其他函数):
module.exports = {
  area: function(width) {
    return width * width;
  },
       
  perimeter: function(width) {
    return 4 * width;
  }
};

使用异步 APIs


JavaScript代码经常使用异步而不是同步 API 来进行操作,这些操作可能需要一些时间才能完成。同步API是一种每个操作必须在下一次操作开始之前完成的API。例如,下列日志函数是同步的,并按顺序将文本打印到控制台(第一、第二)。
console.log('First');
console.log('Second');

相比之下,异步API是启动一个操作并立即返回(在操作完成之前)的一种API。一旦操作完成,API将使用某种机制来执行附加的操作。例如,下面的代码将打印出“Second, First”,因为即使 setTimeout() 方法首先被调用,并且立即返回,操作也不会在几秒钟内完成。
setTimeout(function() {
   console.log('First');
   }, 3000);
console.log('Second');
在Node中使用无阻塞异步API甚至比在浏览器中更为重要,这是因为Node是一个单线程事件驱动的执行环境。单线程意味着所有的对服务器的请求是运行在同一个线程上的,而不是分布在不同的进程上。这个模式对于运行速度与服务器资源来说是非常有效率的,但是这也意味着如果任何一个函数调用同步方法如果使用了很长时间还完不成的话,这种情况就不仅会阻塞当前的请求,而且会阻塞你的Web应用的每个其他的请求。


有多种方法可以为一个异步API来通知你的应用(函数的)执行已结束。最常用的方法是当你调用异步API时注册一个回调函数,这个回调函数当运行结束后被调用。这也是上面的代码所使用的方法。


Tip: 如果您必须按顺序执行一系列依赖异步操作,则使用回调可能会非常“混乱”,因为这会导致多级嵌套回调。 这个问题通常被称为“回调地狱”。 这个问题可以通过良好的编码实践(参考http://callbackhell.com/)减少,使用像async这样的模块,或者使用ES6功能例如 Promise


Note: Node和Express的一个常见约定是使用错误优先回调。在这个约定中,回调函数中的第一个值是一个错误值,而后续的参数则包含成功数据。有一个很好的解释为什么这个方法在这个博客中是有用的:The Node.js Way - Understanding Error-First Callbacks (fredkschott.com).


创建路由处理器


在上面的Hello World Express示例中,我们为HTTP GET请求('/')定义了一个(回调)路由处理函数。
app.get('/', function(req, res) {
  res.send('Hello World!');
});

回调函数接受请求和响应对象作为参数。在这种情况下,该方法只需在响应上调用send()来返回字符串“Hello World!”有许多其他的响应方法可以结束请求/响应周期,例如您可以调用res.json()来发送一个JSON响应,或者res.sendFile()来发送一个文件。

JavaScript提示:在回调函数中可以使用任何参数名;当调用回调时,第一个参数总是请求,第二个参数总是响应。将它们命名为能够识别回调体中正在使用的对象的名称是有意义的。
表达应用程序对象还提供了其他方法来定义路由处理程序HTTP动词,大多以完全相同的方式使用:post(),(),删除(),()选项,跟踪(),复制(),(),锁mkcol(),(),清洗(),propfind(),proppatch(),解锁(),()的报告,mkactivity(),检出(),()合并,m-search(),通知(),订阅(),退订(),(),搜索(),()连接。

有一个特殊的路由方法app.all(),它将在响应任何HTTP方法时被调用。这用于在特定路径上加载所有请求方法的中间件函数。下面的示例(来自Express文档)显示了一个处理程序,该处理程序将对请求/secret执行,而不考虑所使用的HTTP谓词(只要HTTP模块支持它)。
app.all('/secret', function(req, res, next) {
  console.log('Accessing the secret section ...');
  next(); // pass control to the next handler
});

路由允许您匹配URL中特定的字符模式,并从URL提取一些值并将它们作为参数传递给路由处理程序(作为作为参数传递的请求对象的属性)。

通常,将站点特定部分的路由处理程序分组在一起并使用通用的路由前缀访问它们是很有用的(例如,具有Wiki的站点可能在一个文件中包含所有与Wiki相关的路由,并使用/ Wiki /的路由前缀访问它们)。在Express中,这是通过使用Express来实现的。路由器对象。例如,我们可以在一个名为wiki的模块中创建wiki路由。然后导出路由器对象,如下图所示:
// wiki.js - Wiki route module

var express = require('express');
var router = express.Router();

// Home page route
router.get('/', function(req, res) {
  res.send('Wiki home page');
});

// About page route
router.get('/about', function(req, res) {
  res.send('About this wiki');
});

module.exports = router;

注意:向Router对象添加路由就像向app对象添加路由一样(如前面所示)。

要在主应用程序文件中使用路由器,我们需要()路由模块(wiki.js),然后在Express应用程序上调用use(),将路由器添加到中间件处理路径。这两条路径可以从/wiki/和/wiki/about/访问。
var wiki = require('./wiki.js');
// ...
app.use('/wiki', wiki);
我们将向您展示更多关于路由的内容,特别是关于使用路由器的内容,稍后将在链接部分路由和控制器中介绍。

使用中间件


中间件广泛用于Express应用程序,用于从服务静态文件到错误处理到压缩HTTP响应的任务。路由函数通过向HTTP客户机返回一些响应来结束HTTP请求-响应周期,而中间件函数通常对请求或响应执行一些操作,然后调用“堆栈”中的下一个函数,后者可能是更多的中间件或路由处理程序。调用中间件的顺序取决于应用程序开发人员。

注意:中间件可以执行任何操作、执行任何代码、更改请求和响应对象,还可以结束请求-响应周期。如果它没有结束循环,那么它必须调用next()将控制权传递给下一个中间件函数(否则请求将被挂起)。

大多数应用程序将使用第三方中间件来简化常见的web开发任务,如使用cookie、会话、用户身份验证、访问请求POST和JSON数据、日志等。您可以找到Express团队维护的中间件包列表(还包括其他流行的第三方包)。其他快递包裹在NPM包管理器上。

要使用第三方中间件,首先需要使用NPM将其安装到应用程序中。例如,要安装morgan HTTP请求日志记录器中间件,您需要这样做:
$ npm install morgan

然后,您可以调用Express应用程序对象上的use()来将中间件添加到堆栈中:
var express = require('express');
var logger = require('morgan');
var app = express();
app.use(logger('dev'));
...

注意:中间件和路由函数是按照声明的顺序调用的。对于某些中间件,顺序非常重要(例如,如果会话中间件依赖于cookie中间件,那么必须首先添加cookie处理程序)。几乎总是在设置路由之前调用中间件,或者路由处理程序无法访问中间件添加的功能。

您可以编写自己的中间件函数,而且您可能不得不这样做(如果只是为了创建错误处理代码的话)。唯一的区别一个中间件功能和路由处理程序回调是中间件的功能有第三个参数下,中间件功能预计称如果他们没有完成请求周期(中间件函数被调用时,这个包含下一个函数,必须调用)。

您可以使用app.use()或app.add()向处理链添加中间件函数,这取决于您是想将中间件应用于所有响应,还是应用于具有特定HTTP谓词(GET、POST等)的响应。在这两种情况下都指定相同的路由,尽管在调用app.use()时路由是可选的。

下面的示例展示了如何使用这两种方法添加中间件功能,以及使用/不使用路由。
var express = require('express');
var app = express();

// An example middleware function
var a_middleware_function = function(req, res, next) {
  // ... perform some operations
  next(); // Call next() so Express will call the next middleware function in the chain.
}

// Function added with use() for all routes and verbs
app.use(a_middleware_function);

// Function added with use() for a specific route
app.use('/someroute', a_middleware_function);

// A middleware function added for a specific HTTP verb and route
app.get('/', a_middleware_function);

app.listen(3000);

JavaScript提示:在上面,我们分别声明中间件函数,然后将其设置为回调函数。在前面的路由处理程序函数中,我们在使用回调函数时声明了它。在JavaScript中,任何一种方法都是有效的。

Express文档有很多关于使用和编写Express中间件的优秀文档。

提供静态文件


你可以乘快车。为静态文件提供服务的静态中间件,包括您的图像、CSS和JavaScript (static()),是实际上属于Express的唯一中间件功能。例如,您可以使用下面的行来提供来自名为“public”的目录下的图像、CSS文件和JavaScript文件,该目录与您调用node的级别相同:
app.use(express.static('public'));

通过将其文件名(相对于基本“公共”目录)添加到基本URL,可以为公共目录中的任何文件提供服务。举个例子:
http://localhost:3000/images/dog.jpg
http://localhost:3000/css/style.css
http://localhost:3000/js/app.js
http://localhost:3000/about.html

可以多次调用static()来服务多个目录。如果一个文件不能被一个中间件函数找到,那么它将被简单地传递给后续的中间件(调用中间件的顺序基于您的声明顺序)。
app.use(express.static('public'));
app.use(express.static('media'));

您还可以为静态URL创建虚拟前缀,而不是将文件添加到基本URL。例如,这里我们指定了一个挂载路径,以便文件装载前缀“/media”:
app.use('/media', express.static('public'));

现在,您可以从/media path前缀加载公共目录中的文件。
http://localhost:3000/media/images/dog.jpg
http://localhost:3000/media/video/cat.mp4
http://localhost:3000/media/cry.mp3


处理错误


错误由一个或多个具有四个参数的特殊中间件函数处理,而不是通常的三个参数err、req、res、next)。例如:
app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

它们可以返回所需的任何内容,但必须在所有其他app.use()和路由调用之后调用,以便它们是请求处理过程中的最后一个中间件!

Express自带了一个内置的错误处理程序,它可以处理应用程序中可能遇到的任何剩余错误。这个默认的错误处理中间件函数是在中间件函数堆栈的末尾添加的。如果您将一个错误传递给next(),并且没有在错误处理程序中处理它,它将由内置的错误处理程序处理;错误将通过堆栈跟踪写入客户机。

注意:在生产环境中不包含堆栈跟踪。要在生产模式下运行它,需要将环境变量NODE_ENV设置为“production”。

注意:HTTP404和其他“错误”状态代码不被视为错误。如果您想处理这些问题,您可以添加一个中间件函数来完成。有关更多信息,请参阅常见问题。

使用数据库


Express应用程序可以使用Node支持的任何数据库机制(Express本身不为数据库管理定义任何特定的额外行为/需求)。有很多选项,包括PostgreSQL、MySQL、Redis、SQLite、MongoDB等。

为了使用这些,您必须首先使用NPM安装数据库驱动程序。例如,要安装流行的NoSQL MongoDB的驱动程序,您可以使用以下命令:
$ npm install mongodb

数据库本身可以安装在本地或云服务器上。在您的Express代码中,您需要驱动程序,连接到数据库,然后执行创建、读取、更新和删除(CRUD)操作。下面的示例(来自Express文档)展示了如何使用MongoDB找到“哺乳动物”记录。
var MongoClient = require('mongodb').MongoClient;

MongoClient.connect('mongodb://localhost:27017/animals', function(err, db) {
  if (err) throw err;

  db.collection('mammals').find().toArray(function (err, result) {
    if (err) throw err;

    console.log(result);
  });
});

另一种流行的方法是通过对象关系映射器(ORM)间接访问数据库。在这种方法中,您将数据定义为“对象”或“模型”,ORM将这些映射到底层数据库格式。这种方法的好处是,作为开发人员,您可以继续从JavaScript对象而不是数据库语义的角度来考虑问题,并且可以很明显地对传入的数据执行验证和检查。我们将在后面的文章中讨论数据库。


有关更多信息,请参阅数据库集成(Express docs)。

数据挖掘


模板引擎(通过Express称为“视图引擎”)允许您在模板中指定输出文档的结构,使用占位符表示在生成页面时将要填充的数据。模板通常用于创建HTML,但也可以创建其他类型的文档。Express支持许多模板引擎,这里有一个有用的比较更流行的引擎:比较JavaScript模板引擎:Jade, Mustache, Dust等等。

在您的应用程序设置代码中,您设置了要使用的模板引擎以及Express应该使用“视图”和“视图引擎”设置查找模板的位置,如下所示(您还必须安装包含模板库的包!)
var express = require('express');
var app = express();

// Set directory to contain the templates ('views')
app.set('views', path.join(__dirname, 'views'));

// Set view engine to use, in this case 'some_template_engine_name'
app.set('view engine', 'some_template_engine_name');

模板的外观将取决于您使用的引擎。假设您有一个名为“index”的模板文件。"包含数据变量'title'和' message '的占位符,您可以在路由处理函数中调用response .render()来创建和发送HTML响应:
app.get('/', function(req, res) {
  res.render('index', { title: 'About dogs', message: 'Dogs rock!' });
});


文件结构


Express对结构和使用的组件没有任何假设。路由、视图、静态文件和其他特定于应用程序的逻辑可以存在于任意数量的具有任意目录结构的文件中。虽然很可能整个表达应用程序在一个文件,通常是有意义的应用程序分割成文件基于函数(例如:账户管理、博客、论坛)和架构问题域(如模型、视图或控制器如果你碰巧使用MVC体系结构)。

在后面的主题中,我们将使用Express Application Generator,它创建了一个模块化的应用程序框架,我们可以轻松地扩展它来创建web应用程序。


分享至 : QQ空间
收藏

0 个回复

您需要登录后才可以回帖 登录 | 立即注册