Koa.js - 快速指南


Koa.js - 概述

Web 应用程序框架为您提供了一个简单的 API 来构建网站、Web 应用程序和后端。您无需担心低级协议、流程等。

相思树是什么?

Koa 提供了构建应用程序的最小界面。它是一个非常小的框架(600 LoC),提供了构建应用程序所需的工具并且非常灵活。npm 上有许多适用于 Koa 的模块,可以直接插入其中。Koa 可以被认为是express.js 的核心,没有任何花里胡哨的东西。

为什么是科阿?

Koa 占用空间小(600 LoC),是节点上非常薄的抽象层,用于创建服务器端应用程序。它是完全可插拔的,并且拥有庞大的社区。这也让我们可以很方便地扩展Koa并根据我们的需求使用它。它是使用前沿技术 (ES6) 构建的,这使其比 Express 等旧框架更具优势。

哈巴狗

Pug(以前称为 Jade)是一种用于编写 HTML 模板的简洁语言。

  • 生成 HTML
  • 支持动态代码
  • 支持可重用性 (DRY)

它是 Koa 中最流行的模板语言之一。

MongoDB 和猫鼬

MongoDB 是一个开源文档数据库,旨在简化开发和扩展。我们将使用该数据库来存储数据。

Mongoose 是 Node.js 的客户端 API,它使我们可以轻松地从 Koa 应用程序访问我们的数据库。

Koa.js - 环境

要开始使用 Koa 框架进行开发,您需要安装 Node 和 npm(节点包管理器)。如果您还没有这些,请转到节点设置以在本地系统上安装节点。通过在终端中运行以下命令来确认已安装 Node 和 npm。

$ node --version
$ npm --version

您应该收到类似于以下内容的输出 -

v5.0.0
3.5.2

请确保您的节点版本高于6.5.0。现在我们已经设置了 Node 和 npm,让我们了解一下 npm 是什么以及如何使用它。

节点包管理器 (npm)

npm 是 Node 的包管理器。npm 注册表是 Node.js、前端 Web 应用程序、移动应用程序、机器人、路由器以及 JavaScript 社区无数其他需求的开源代码包的公共集合。npm 允许我们访问所有这些包并在本地安装它们。您可以在npmJS浏览 npm 上可用的软件包列表。

如何使用npm?

使用 npm 安装软件包有两种方法:全局安装和本地安装。

全局- 此方法通常用于安装开发工具和基于 CLI 的软件包。要全局安装软件包,请使用以下命令。

$ npm install -g <package-name>

本地- 此方法通常用于安装框架和库。本地安装的软件包只能在其安装的目录中使用。要在本地安装软件包,请使用与上面相同的命令,但不带 - g标志。

$ npm install <package-name>

每当我们使用 npm 创建项目时,我们都需要提供一个 package.json 文件,其中包含有关我们项目的所有详细信息。npm 使我们可以轻松设置此文件。让我们建立我们的开发项目。

步骤 1 - 启动终端/cmd,创建一个名为 hello-world 的新文件夹并 cd 进入其中 -

环境 mkdir

步骤 2 - 现在要使用 npm 创建 package.json 文件,请使用以下命令。

npm init

它会要求您提供以下信息 -

环境 NPM

只需按住 Enter 键,然后在“作者姓名”字段中输入您的姓名即可。

步骤 3 - 现在我们已经设置了 package.json 文件,我们将安装 Koa。要安装 Koa 并将其添加到我们的 package.json 文件中,请使用以下命令。

$ npm install --save koa

要确认 Koa 安装正确,请运行以下命令。

$ ls node_modules #(dir node_modules for windows)

提示- --save标志可以替换为-S标志。该标志确保 Koa 作为依赖项添加到我们的 package.json 文件中。这样做有一个好处,下次我们需要安装项目的所有依赖项时,我们只需要运行命令 npm install ,它就会在这个文件中找到依赖项并为我们安装它们。

这就是我们开始使用 Koa 框架进行开发所需的全部内容。为了使我们的开发过程变得更加容易,我们将安装来自 npm 的工具,nodemon。这个工具的作用是,一旦我们对任何文件进行更改,它就会重新启动服务器,否则我们需要在每次文件修改后手动重新启动服务器。要安装nodemon,请使用以下命令。

$ npm install -g nodemon

现在我们已经准备好深入 Koa 了!

Koa.js - 你好世界

一旦我们完成了开发,就可以开始使用 Koa 开发我们的第一个应用程序了。创建一个名为app.js的新文件并在其中键入以下内容。

var koa = require('koa');
var app = new koa();

app.use(function* (){
   this.body = 'Hello world!';
});

app.listen(3000, function(){
   console.log('Server running on https://localhost:3000')
});

保存文件,转到终端并输入。

$ nodemon app.js

这将启动服务器。要测试此应用程序,请打开浏览器并转到https://localhost:3000,您应该会收到以下消息。

你好世界

这个应用程序如何工作?

第一行在我们的文件中导入 Koa。我们可以通过变量 Koa 访问它的 API。我们用它来创建一个应用程序并将其分配给 var app。

app.use(function) - 这个函数是一个中间件,每当我们的服务器收到请求时就会调用它。我们将在后续章节中了解有关中间件的更多信息。回调函数是一个生成器,我们将在下一章中看到。这个生成器的上下文在 Koa 中被称为 context。该上下文用于访问和修改请求和响应对象。我们将此响应的正文设置为Hello world!

app.listen(port, function) - 此函数绑定并侦听指定端口上的连接。端口是此处唯一必需的参数。如果应用程序运行成功,则执行回调函数。

Koa.js - 生成器

JavaScript ES6 最令人兴奋的新功能之一是一种新的函数,称为生成器。在生成器之前,整个脚本通常按从上到下的顺序执行,没有一种简单的方法来停止代码执行并稍后使用相同的堆栈恢复。生成器是可以退出并稍后重新进入的函数。它们的上下文(变量绑定)将在重新进入时保存。

生成器允许我们在中间停止代码执行。因此,让我们看一下一个简单的生成器。

var generator_func = function* (){
   yield 1;
   yield 2;
};

var itr = generator_func();
console.log(itr.next());
console.log(itr.next());
console.log(itr.next());

运行上面的代码,结果如下。

{ value: 1, done: false }
{ value: 2, done: false }
{ value: undefined, done: true }

让我们看看上面的代码。我们首先创建一个名为generator_func()的生成器。我们创建了这个看起来奇怪的函数的实例并将其分配给itr。然后我们开始对该 itr 变量调用next() 。

调用 next() 启动生成器并运行直到达到产量。然后它返回带有 value 的对象并完成,其中 value 具有表达式值。这个表达式可以是任何东西。此时,它暂停执行。同样,当我们调用这个函数(next)时,生成器从上一个让出点恢复执行,函数状态与暂停时相同,直到下一个让出点。这样做直到代码中不再有屈服点为止。

科阿的发电机

那么我们为什么要在本教程中讨论生成器呢?您可能还记得 hello world 程序中的内容,我们使用function* ()表示法将回调传递给 app.use()。Koa 是一个对象,它包含一系列中间件生成器函数,所有这些函数都根据每个请求以类似堆栈的方式组合和执行。Koa 还实现了控制流的下游和上游。

查看以下示例以更好地理解这一点。

var koa = require('koa');
var app = koa();
 
app.use(function* (next) {
   //do something before yielding to next generator function 
   
   //in line which will be 1st event in downstream
   console.log("1");
   yield next;
 
   //do something when the execution returns upstream, 
   //this will be last event in upstream
   console.log("2");
});
app.use(function* (next) {
   // This shall be 2nd event downstream
   console.log("3");
   yield next;
 
   // This would be 2nd event upstream
   console.log("4");
});
app.use(function* () { 
   // Here it would be last function downstream
   console.log("5");
   
   // Set response body
   this.body = "Hello Generators";

   // First event of upstream (from the last to first)
   console.log("6");
});

app.listen(3000);

运行上述代码并导航到https://localhost:3000/时,我们在控制台上得到以下输出。

1
3
5
6
4
2

这本质上就是 Koa 使用生成器的方式。它允许我们使用此属性创建紧凑的中间件,并为上游和下游功能编写代码,从而使我们免于回调。

Koa.js - 路由

Web 框架以不同的路径提供 HTML 页面、脚本、图像等资源。Koa 核心模块不支持路由。我们需要使用 Koa-router 模块来轻松地在 Koa 中创建路由。使用以下命令安装此模块。

npm install --save koa-router

现在我们已经安装了 Koa-router,让我们看一个简单的 GET 路由示例。

var koa = require('koa');
var router = require('koa-router');
var app = koa();

var _ = router();              //Instantiate the router
_.get('/hello', getMessage);   // Define routes

function *getMessage() {
   this.body = "Hello world!";
};

app.use(_.routes());           //Use the routes defined using the router
app.listen(3000);

如果我们运行应用程序并转到 localhost:3000/hello,服务器会在路由“/hello”处收到一个 get 请求。我们的 Koa 应用程序执行附加到该路由的回调函数并发送“Hello World!” 作为回应。

路由你好

我们还可以在同一条路线上有多种不同的方法。例如,

var koa = require('koa');
var router = require('koa-router');
var app = koa();

var _ = router(); //Instantiate the router

_.get('/hello', getMessage);
_.post('/hello', postMessage);

function *getMessage() {
	this.body = "Hello world!";
};
function *postMessage() {
   this.body = "You just called the post method at '/hello'!\n";
};
app.use(_.routes()); //Use the routes defined using the router
app.listen(3000);

要测试此请求,请打开终端并使用 cURL 执行以下请求

curl -X POST "https://localhost:3000/hello"

卷曲路由

Express 提供了一种特殊方法all ,用于使用相同的函数处理特定路由上的所有类型的 http 方法。要使用此方法,请尝试以下操作 -

_.all('/test', allMessage);

function *allMessage(){
   this.body = "All HTTP calls regardless of the verb will get this response";
};

Koa.js - URL 构建

我们现在可以定义路线;它们要么是静态的,要么是固定的。要使用动态路由,我们需要提供不同类型的路由。使用动态路由允许我们传递参数并基于它们进行处理。以下是动态路线的示例。

var koa = require('koa');
var router = require('koa-router');
var app = koa();

var _ = router();

_.get('/:id', sendID);

function *sendID() {
   this.body = 'The id you specified is ' + this.params.id;
}

app.use(_.routes());
app.listen(3000);

要测试此功能,请转到https://localhost:3000/123。您将得到以下响应。

URL 建筑物 ID

您可以将 URL 中的“123”替换为其他内容,它将反映在响应中。以下是上述内容的一个复杂示例。

var koa = require('koa');
var router = require('koa-router');
var app = koa();

var _ = router();

_.get('/things/:name/:id', sendIdAndName);

function *sendIdAndName(){
   this.body = 'id: ' + this.params.id + ' and name: ' + this.params.name;
};

app.use(_.routes());

app.listen(3000);

要测试此功能,请转到https://localhost:3000/things/tutorialspoint/12345

URL 构建复合体

您可以使用this.params对象来访问您在 URL 中传递的所有参数。注意,上面两个有不同的路径。它们永远不会重叠。另外,如果你想在获得“/things”时执行代码,那么你需要单独定义它。

模式匹配的路线

您还可以使用正则表达式来限制 URL 参数匹配。假设您需要 id 为五位数字。您可以使用以下路由定义。

var koa = require('koa');
var router = require('koa-router');
var app = koa();

var _ = router();

_.get('/things/:id([0-9]{5})', sendID);

function *sendID(){
   this.body = 'id: ' + this.params.id;
}

app.use(_.routes());
app.listen(3000);

请注意,这只会匹配具有 5 位长 id 的请求。您可以使用更复杂的正则表达式来匹配/验证您的路线。如果您的路由均不匹配请求,您将收到“未找到”消息作为响应。

例如,如果我们定义与上面相同的路由,则在使用有效 URL 请求时,我们会得到 -

URL匹配正确

Koa.js - HTTP 方法

HTTP 方法在请求中提供,并指定客户端请求的操作。下表总结了常用的 HTTP 方法。

先生。 方法及说明
1

得到

GET 方法请求指定资源的表示。使用 GET 的请求应该只检索数据并且不应该有其他效果。

2

邮政

POST 方法请求服务器接受请求中包含的数据作为 URI 标识的资源的新对象/实体。

3

PUT 方法请求服务器接受请求中包含的数据,作为对 URI 标识的现有对象的修改。如果它不存在,则 PUT 方法应该创建一个。

4

删除

DELETE方法请求服务器删除指定的资源。

这些是最常见的 HTTP 方法。要了解有关它们的更多信息,请访问https://www.tutorialspoint.com/http/http_methods.htm

Koa.js - 请求对象

Koa Request 对象是 Node 的普通请求对象之上的抽象,提供对日常 HTTP 服务器开发有用的附加功能。Koa 请求对象嵌入在上下文对象this中。每当收到请求时,我们就注销请求对象。

var koa = require('koa');
var router = require('koa-router');
var app = koa();

var _ = router();

_.get('/hello', getMessage);

function *getMessage(){
   console.log(this.request);
   this.body = 'Your request has been logged.';
}
app.use(_.routes());
app.listen(3000);

当您运行此代码并导航到https://localhost:3000/hello时,您将收到以下响应。

请求对象

在您的控制台上,您将注销请求对象。

{ 
   method: 'GET',
   url: '/hello/',
   header: 
   { 
      host: 'localhost:3000',
      connection: 'keep-alive',
      'upgrade-insecure-requests': '1',
      'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) 
         AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36',
      accept: 'text/html,application/xhtml+xml,
         application/xml;q = 0.9,image/webp,*/*;q = 0.8',
      dnt: '1',
      'accept-encoding': 'gzip, deflate, sdch',
      'accept-language': 'en-US,en;q = 0.8' 
   }
}

我们可以使用该对象访问请求的许多有用属性。让我们看一些例子。

请求头

提供所有请求标头。

请求方法

提供请求方法(GET、POST等)

请求.href

提供完整的请求 URL。

请求路径

提供请求的路径。没有查询字符串和基本 url。

请求.查询

给出已解析的查询字符串。例如,如果我们将其记录在https://localhost:3000/hello/?name=Ayush&age=20&country=India 等请求上,那么我们将获得以下对象。

{
   name: 'Ayush',
   age: '20',
   country: 'India'
}

请求.接受(类型)

该函数根据请求的资源是否接受给定的请求类型返回 true 或 false。

您可以在Request文档中阅读有关请求对象的更多信息。

Koa.js - 响应对象

Koa Response 对象是 Node 的普通响应对象之上的抽象,提供对日常 HTTP 服务器开发有用的附加功能。Koa 响应对象嵌入到上下文对象this中。每当收到请求时,我们就注销响应对象。

var koa = require('koa');
var router = require('koa-router');
var app = koa();

var _ = router();

_.get('/hello', getMessage);

function *getMessage(){
   this.body = 'Your request has been logged.';
   console.log(this.response);
}

app.use(_.routes());
app.listen(3000);

当您运行此代码并导航到https://localhost:3000/hello时,您将收到以下响应。

请求对象

在您的控制台上,您将注销请求对象。

{ 
   status: 200,
   message: 'OK',
   header: 
   {
      'content-type': 'text/plain; charset=utf-8',
      'content-length': '12' 
   },
   body: 'Your request has been logged.' 
}

状态和消息由 Koa 自动设置,但可以由我们修改。如果我们不设置响应正文,状态代码将设置为 404。一旦设置响应正文,状态代码将默认设置为 200。我们可以明确地重写此Behave。

我们可以使用该对象访问响应的许多有用属性。让我们看一些例子 -

响应头

提供所有响应标头。

响应状态

提供响应状态(200、404、500 等)。该属性还用于设置响应状态。

响应消息

提供响应消息。此属性还用于设置带有响应的自定义消息。它与response.status 相关。

响应体

获取或设置响应正文。通常,我们使用上下文对象来访问它。这只是访问它的另一种方式。主体的类型可以是:String、Buffer、Stream、Object 或 Null。

响应类型

获取或设置当前响应的内容类型。

响应.get(字段)

该函数用于获取具有不区分大小写值字段的标头的值。

响应.set(字段,值)

此函数用于使用字段和值对在响应上设置标头。

响应.删除(字段)

此函数用于使用字段名称取消设置响应上的标头。

您可以在Response文档中阅读有关响应对象的更多信息。

Koa.js - 重定向

创建网站时重定向非常重要。如果请求的 URL 格式错误或者您的服务器出现一些错误,您应该将它们重定向到相应的错误页面。重定向还可用于防止人们进入您网站的限制区域。

让我们创建一个错误页面,并在有人请求格式错误的 URL 时重定向到该页面。

var koa = require('koa');
var router = require('koa-router');
var app = koa();
var _ = router();

_.get('/not_found', printErrorMessage);
_.get('/hello', printHelloMessage);

app.use(_.routes());
app.use(handle404Errors);

function *printErrorMessage() {
   this.status = 404;
   this.body = "Sorry we do not have this resource.";
}
function *printHelloMessage() {
   this.status = 200;
   this.body = "Hey there!";
}
function *handle404Errors(next) {
   if (404 != this.status) return;
   this.redirect('/not_found');
}
app.listen(3000);

当我们运行此代码并导航到 /hello 之外的任何路由时,我们将被重定向到 /not_found。我们将中间件放在最后(app.use 函数调用此中间件)。这确保我们最终到达中间件并发送相应的响应。以下是运行上述代码时看到的结果。

当我们导航到https://localhost:3000/hello时,我们得到 -

重定向你好

如果我们导航到任何其他路线,我们会得到 -

重定向错误

Koa.js - 错误处理

错误处理在构建 Web 应用程序中起着重要作用。Koa 也使用中间件来实现此目的。

在 Koa 中,您添加一个中间件,它确实尝试 {yield next}作为第一个中间件之一。如果我们在下游遇到任何错误,我们将返回到关联的 catch 子句并在此处处理错误。例如 -

var koa = require('koa');
var app = koa();

//Error handling middleware
app.use(function *(next) {
   try {
      yield next;
   } catch (err) {
      this.status = err.status || 500;
      this.body = err.message;
      this.app.emit('error', err, this);
   }
});

//Create an error in the next middleware
//Set the error message and status code and throw it using context object

app.use(function *(next) {
   //This will set status and message
   this.throw('Error Message', 500);
});

app.listen(3000);

我们故意在上面的代码中创建了一个错误,并在第一个中间件的 catch 块中处理该错误。然后将其发送到我们的控制台并作为响应发送给我们的客户端。以下是触发此错误时收到的错误消息。

InternalServerError: Error Message
   at Object.module.exports.throw 
      (/home/ayushgp/learning/koa.js/node_modules/koa/lib/context.js:91:23)
   at Object.<anonymous> (/home/ayushgp/learning/koa.js/error.js:18:13)
   at next (native)
   at onFulfilled (/home/ayushgp/learning/koa.js/node_modules/co/index.js:65:19)
   at /home/ayushgp/learning/koa.js/node_modules/co/index.js:54:5
   at Object.co (/home/ayushgp/learning/koa.js/node_modules/co/index.js:50:10)
   at Object.toPromise (/home/ayushgp/learning/koa.js/node_modules/co/index.js:118:63)
   at next (/home/ayushgp/learning/koa.js/node_modules/co/index.js:99:29)
   at onFulfilled (/home/ayushgp/learning/koa.js/node_modules/co/index.js:69:7)
   at /home/ayushgp/learning/koa.js/node_modules/co/index.js:54:5

现在发送到服务器的任何请求都会导致此错误。

Koa.js - 级联

中间件函数是可以访问上下文对象和应用程序请求-响应周期中的下一个中间件函数的函数。这些函数用于修改请求和响应对象以执行诸如解析请求正文、添加响应标头等任务。Koa 更进一步,生成'downstream',然后将控制流回'upstream'。这种效应称为级联

以下是中间件功能实际运行的简单示例。

var koa = require('koa');
var app = koa();
var _ = router();

//Simple request time logger
app.use(function* (next) {
   console.log("A new request received at " + Date.now());
   
   //This function call is very important. It tells that more processing is 
   //required for the current request and is in the next middleware function/route handler.
   yield next;
});

app.listen(3000);

服务器上的每个请求都会调用上述中间件。因此,每次请求后,我们都会在控制台中收到以下消息。

A new request received at 1467267512545

要将其限制为特定路由(及其所有子路由),我们只需要像路由一样创建路由。实际上这些中间件只是处理我们的请求。

例如,

var koa = require('koa');
var router = require('koa-router');
var app = koa();

var _ = router();

//Simple request time logger
_.get('/request/*', function* (next) {
   console.log("A new request received at " + Date.now());
   yield next;
});

app.use(_.routes());
app.listen(3000);

现在,每当您请求“/request”的任何子路由时,它才会记录时间。

中间件调用顺序

Koa 中中间件最重要的事情之一是它们在文件中写入/包含的顺序就是它们在下游执行的顺序。一旦我们在中间件中点击了yield语句,它就会切换到下一个中​​间件,直到到达最后一个。然后我们再次开始从yield语句返回并恢复函数。

例如,在下面的代码片段中,第一个函数首先执行直到yield,然后执行第二个中间件直到yield,然后是第三个。由于这里不再有中间件,因此我们开始向上移动,以相反的顺序执行,即第三、第二、第一。这个例子总结了如何以 Koa 的方式使用中间件。

var koa = require('koa');
var app = koa();

//Order of middlewares
app.use(first);
app.use(second);
app.use(third);

function *first(next) {
   console.log("I'll be logged first. ");
   
   //Now we yield to the next middleware
   yield next;
   
   //We'll come back here at the end after all other middlewares have ended
   console.log("I'll be logged last. ");
};

function *second(next) {
   console.log("I'll be logged second. ");
   yield next;
   console.log("I'll be logged fifth. ");
};

function *third(next) {
   console.log("I'll be logged third. ");
   yield next;
   console.log("I'll be logged fourth. ");
};

app.listen(3000);

当我们运行此代码后访问“/”时,在我们的控制台上我们将得到 -

I'll be logged first. 
I'll be logged second. 
I'll be logged third. 
I'll be logged fourth. 
I'll be logged fifth. 
I'll be logged last. 

下图总结了上面示例中实际发生的情况。

中间件描述

现在我们知道如何创建自己的中间件,让我们讨论一些最常用的社区创建的中间件。

第三方中间件

此处提供了用于 Express 的第三方中间件列表以下是一些最常用的中间件 -

  • koa-bodyparser
  • koa路由器
  • 相思树静态
  • koa 压缩

我们将在后续章节中讨论多个中间件。

Koa.js - 模板

Pug 是一个模板引擎。模板引擎用于消除服务器代码与 HTML 的混乱,将字符串与现有的 HTML 模板连接起来。Pug 是一个非常强大的模板引擎,它具有多种功能,例如过滤器、包含、继承、插值等。这方面有很多基础知识需要涵盖。

要将 Pug 与 Koa 一起使用,我们需要使用以下命令安装它。

$ npm install --save pug koa-pug

安装 pug 后,将其设置为应用程序的模板引擎。将以下代码添加到您的 app.js 文件中。

var koa = require('koa');
var router = require('koa-router');
var app = koa();

var Pug = require('koa-pug');
var pug = new Pug({
   viewPath: './views',
   basedir: './views',
   app: app //Equivalent to app.use(pug)
});

var _ = router(); //Instantiate the router

app.use(_.routes()); //Use the routes defined using the router
app.listen(3000);

现在,创建一个名为views的新目录。在该目录中,创建一个名为first_view.pug 的文件,并在其中输入以下数据。

doctype html
html
   head
      title = "Hello Pug"
   body
      p.greetings#people Hello Views!

要运行此页面,请将以下路由添加到您的应用程序。

_.get('/hello', getMessage); // Define routes

function *getMessage(){
   this.render('first_view');
};

您将收到的输出为 -

你好观点

Pug 所做的是将这个看起来非常简单的标记转换为 html。我们不需要跟踪关闭标签,不需要使用 class 和 id 关键字,而是使用 '.' 和“#”来定义它们。上面的代码首先被转换为

<!DOCTYPE html>
<html>
   <head>
      <title>Hello Pug</title>
   </head>
    
   <body>
      <p class = "greetings" id = "people">Hello Views!</p>
   </body>
</html>

Pug 的能力远不止简化 HTML 标记。让我们来探讨一下 Pug 的一些功能。

简单标签

标签根据其缩进进行嵌套。就像上面的示例一样,<title>在<head>标记内缩进,因此它位于其中。然而,<body>标签位于相同的缩进位置,因此它是<head>标签的同级标签。

我们不需要关闭标签。一旦 Pug 在相同或外部缩进级别遇到下一个标签,它就会为我们关闭该标签。

有三种方法可以将文本放入标签内 -

  • 空间分隔 -
h1 Welcome to Pug
  • 管道文本 -
div
   | To insert multiline text, 
   | You can use the pipe operator.
  • 文本块 -
div.
   But that gets tedious if you have a lot of text. 
   You can use "." at the end of tag to denote block of text. 
   To put tags inside this block, simply enter tag in a new line and 
   indent it accordingly.

评论

Pug 使用与 JavaScript(//) 相同的语法来创建注释。这些注释会转换为 html 注释(<!--comment-->)。例如,

//This is a Pug comment

该评论被转换为 -

<!--This is a Pug comment-->

属性

为了定义属性,我们在括号中使用逗号分隔的属性列表。类和 ID 属性具有特殊的表示形式。以下代码行涵盖了给定 html 标签的属性、类和 id 的定义。

div.container.column.main#division(width = "100",height = "100")

这行代码被转换为 -

<div class = "container column main" id = "division" width = "100" height = "100"></div>

将值传递给模板

当我们渲染 Pug 模板时,我们实际上可以从路由处理程序向其传递一个值,然后我们可以在模板中使用该值。使用以下代码创建一个新的路由处理程序。

var koa = require('koa');
var router = require('koa-router');
var app = koa();

var Pug = require('koa-pug');
var pug = new Pug({
   viewPath: './views',
   basedir: './views',
   app: app // equals to pug.use(app) and app.use(pug.middleware)
});

var _ = router(); //Instantiate the router

_.get('//dynamic_view', dynamicMessage); // Define routes

function *dynamicMessage(){
   this.render('dynamic', {
      name: "TutorialsPoint", 
      url:"https://www.tutorialspoint.com"
   });
};

app.use(_.routes()); //Use the routes defined using the router
app.listen(3000);

然后,使用以下代码在views目录中创建一个名为dynamic.pug的新视图文件。

html
   head
      title = name
   body
      h1 = name
      a(href = url) URL

在浏览器中打开localhost:3000/dynamic,以下应该是输出。-

模板变量

我们还可以在文本中使用这些传递的变量。要在标签文本之间插入传递的变量,我们使用 #{variableName} 语法。例如,在上面的例子中,如果我们想从TutorialsPoint插入Greetings,那么我们必须使用以下代码。

html
   head
      title = name
   body
      h1 Greetings from #{name}
      a(href = url) URL

这种使用值的方法称为插值法。

条件句

我们也可以使用条件语句和循环结构。考虑这个实际的例子,如果用户登录,我们希望显示“嗨,用户”,如果没有,那么我们希望向他显示“登录/注册”链接。为了实现这一点,我们可以定义一个简单的模板,例如 -

html
   head
      title Simple template
   body
      if(user)
         h1 Hi, #{user.name}
      else
         a(href = "/sign_up") Sign Up

当我们使用我们的路线渲染它时,如果我们传递一个像这样的对象 -

this.render('/dynamic',{user: 
   {name: "Ayush", age: "20"}
});

它会给出一条消息,显示“嗨,Ayush”。但是,如果我们不传递任何对象或传递没有用户密钥的对象,那么我们将获得一个注册链接。

包含和组件

Pug 提供了一种非常直观的方式来创建网页组件。例如,如果您看到一个新闻网站,带有徽标和类别的标题始终是固定的。我们可以使用包含,而不是将其复制到每个视图。以下示例展示了我们如何使用包含 -

使用以下代码创建三个视图 -

标头.pug

div.header.
   I'm the header for this website.

内容.pug

html
   head
      title Simple template
   body
      include ./header.pug
      h3 I'm the main content
      include ./footer.pug

页脚.pug

div.footer.
   I'm the footer for this website.

为此创建一条路线,如下所示。

var koa = require('koa');
var router = require('koa-router');
var app = koa();

var Pug = require('koa-pug');
var pug = new Pug({
   viewPath: './views',
   basedir: './views',
   app: app //Equivalent to app.use(pug)
});

var _ = router(); //Instantiate the router

_.get('/components', getComponents);

function *getComponents(){
   this.render('content.pug');
}

app.use(_.routes()); //Use the routes defined using the router
app.listen(3000);

转到localhost:3000/components,您应该得到以下输出。

模板化组件

include还可用于包含纯文本、CSS 和 JavaScript。

帕格还有许多其他功能。但是,这些超出了本教程的范围。您可以在Pug进一步探索 Pug 。

Koa.js - 表单数据

表单是网络不可或缺的一部分。几乎我们访问的每个网站都提供为我们提交或获取一些信息的表格。要开始使用表单,我们首先安装 koa-body。要安装它,请转到您的终端并使用 -

$ npm install --save koa-body

将 app.js 文件内容替换为以下代码。

var koa = require('koa');
var router = require('koa-router');
var bodyParser = require('koa-body');
var app = koa();

//Set up Pug
var Pug = require('koa-pug');
var pug = new Pug({
   viewPath: './views',
   basedir: './views',
   app: app //Equivalent to app.use(pug)
});

//Set up body parsing middleware
app.use(bodyParser({
   formidable:{uploadDir: './uploads'},
   multipart: true,
   urlencoded: true
}));

_.get('/', renderForm);
_.post('/', handleForm);

function * renderForm(){
   this.render('form');
}
function *handleForm(){
   console.log(this.request.body);
   console.log(this.req.body);
   this.body = this.request.body; //This is where the parsed request is stored
}

app.use(_.routes()); 
app.listen(3000);

我们在这里做的新事情是导入主体解析器和 multer。我们使用 body 解析器来解析 json 和 x-www-form-urlencoded 标头请求,同时使用 multer 来解析 multipart/form-data。

让我们创建一个 html 表单来测试一下!使用以下代码创建一个名为 form.pug 的新视图。

html
   head
      title Form Tester
   body
      form(action = "/", method = "POST")
         div
            label(for = "say") Say: 
            input(name = "say" value = "Hi")
         br
         div
            label(for = "to") To: 
            input(name = "to" value = "Koa form")
         br
         button(type = "submit") Send my greetings

使用运行您的服务器 -

nodemon index.js

现在转到 localhost:3000/ 并根据需要填写表格,然后提交。您将收到如下响应:

表格已收到

看一下你的控制台,它会以 JavaScript 对象的形式向你显示请求的正文。例如 -

表单控制台

this.request.body对象包含已解析的请求正文。要使用该对象中的字段,只需将它们用作普通 JS 对象即可。

这只是发送请求的一种方式。还有很多其他方法,但这些与这里无关,因为我们的 Koa 应用程序将以相同的方式处理所有这些请求。要了解有关提出请求的不同方式的更多信息,请查看页面。

Koa.js - 文件上传

Web 应用程序需要提供允许文件上传的功能。让我们看看如何从客户端接收文件并将它们存储在我们的服务器上。

我们已经使用 koa-body 中间件来解析请求。该中间件还用于处理文件上传。让我们创建一个表单,允许我们上传文件,然后使用 Koa 保存这些文件。首先创建一个名为file_upload.pug的模板,其中包含以下内容。

html
   head
      title File uploads
   body
      form(action = "/upload" method = "POST" enctype = "multipart/form-data")
         div
            input(type = "text" name = "name" placeholder = "Name")
         
         div
            input(type = "file" name = "image")
         
         div
            input(type = "submit")

请注意,您需要在表单中提供与上面相同的编码类型。现在让我们在服务器上处理这些数据。

var koa = require('koa');
var router = require('koa-router');
var bodyParser = require('koa-body');
var app = koa();

//Set up Pug
var Pug = require('koa-pug');
var pug = new Pug({
   viewPath: './views',
   basedir: './views',
   app: app 
});

//Set up body parsing middleware
app.use(bodyParser({
   formidable:{uploadDir: './uploads'},    //This is where the files would come
   multipart: true,
   urlencoded: true
}));

var _ = router(); //Instantiate the router

_.get('/files', renderForm);
_.post('/upload', handleForm);

function * renderForm(){
   this.render('file_upload');
}

function *handleForm(){
   console.log("Files: ", this.request.body.files);
   console.log("Fields: ", this.request.body.fields);
   this.body = "Received your data!"; //This is where the parsed request is stored
}

app.use(_.routes()); 
app.listen(3000);

当您运行它时,您会得到以下表格。

文件上传表格

当您提交此内容时,您的控制台将产生以下输出。

文件控制台屏幕

上传的文件存储在上述输出的路径中。您可以使用this.request.body.files访问请求中的文件,并通过this.request.body.fields访问该请求中的字段。

Koa.js - 静态文件

静态文件是客户端从服务器下载的文件。创建一个新目录public。Express 默认情况下不允许您提供静态文件。

我们需要一个中间件来达到这个目的。继续安装koa-serve -

$ npm install --save koa-static

现在我们需要使用这个中间件。在此之前创建一个名为 public 的目录。我们将在这里存储所有静态文件。这使我们能够保证服务器代码的安全,因为客户端无法访问此公共文件夹之上的任何内容。创建公共目录后,在其中创建一个名为hello.txt的文件,其中包含您喜欢的任何内容。现在将以下内容添加到您的 app.js 中。

var serve = require('koa-static');
var koa = require('koa');
var app = koa();

app.use(serve('./public'));

app.listen(3000);

注意- Koa 查找相对于静态目录的文件,因此静态目录的名称不是 URL 的一部分。根路由现在设置为您的公共目录,因此您加载的所有静态文件都将被视为公共目录。要测试这是否正常工作,请运行您的应用程序并访问https://localhost:3000/hello.txt

您应该得到以下输出。请注意,这不是 HTML 文档或 Pug 视图,而是一个简单的 txt 文件。

静态文件

多个静态目录

我们还可以使用设置多个静态资产目录 -

var serve = require('koa-static');
var koa = require('koa');
var app = koa();

app.use(serve('./public'));
app.use(serve('./images'));

app.listen(3000);

现在,当我们请求文件时,Koa 将搜索这些目录并向我们发送匹配的文件。

Koa.js - Cookie

Cookie 是简单的小文件/数据,通过服务器请求发送到客户端并存储在客户端。每次用户重新加载网站时,此 cookie 都会随请求一起发送。这有助于跟踪用户的操作。HTTP Cookie 有多种用途。

  • 会话管理
  • 个性化(推荐系统)
  • 用户追踪

要在 Koa 中使用 cookie,我们有以下函数:ctx.cookies.set()ctx.cookies.get()。要设置新的 cookie,让我们在 Koa 应用程序中定义一个新的路由。

var koa = require('koa');
var router = require('koa-router');
var app = koa();

_.get('/', setACookie);

function *setACookie() {
   this.cookies.set('foo', 'bar', {httpOnly: false});
}

var _ = router();

app.use(_.routes());
app.listen(3000);

要检查 cookie 是否已设置,只需转到浏览器,启动控制台,然后输入 -

console.log(document.cookie);

这将产生以下输出(您可能由于浏览器中的扩展而设置了更多 cookie)。

"foo = bar"

这是上面的一个例子。

曲奇饼

浏览器每次查询服务器时也会发回cookie。要查看服务器上的 cookie,请在服务器控制台的路由中将以下代码添加到该路由。

console.log('Cookies: foo = ', this.cookies.get('foo'));

下次您向此路由发送请求时,您将获得以下输出。

Cookies: foo = bar

添加带有过期时间的 Cookie

您可以添加过期的 cookie。要添加过期的 cookie,只需传递一个对象,并将属性“expires”设置为您希望其过期的时间。例如,

var koa = require('koa');
var router = require('koa-router');
var app = koa();

_.get('/', setACookie);

function *setACookie(){
   //Expires after 360000 ms from the time it is set.
	this.cookies.set('name', 'value', { 
      httpOnly: false, expires: 360000 + Date.now() });
}

var _ = router();

app.use(_.routes());
app.listen(3000);

删除现有的 Cookie

要取消设置 cookie,只需将 cookie 设置为空字符串即可。例如,如果您需要清除名为foo的 cookie ,请使用以下代码。

var koa = require('koa');
var router = require('koa-router');
var app = koa();

_.get('/', setACookie);

function *setACookie(){
   //Expires after 360000 ms from the time it is set.
   this.cookies.set('name', '');
}

var _ = router();

app.use(_.routes());
app.listen(3000);

这将取消设置上述 cookie。请注意,当不在客户端代码中使用 cookie 时,应将HttpOnly选项保留为 true。

Koa.js - 会话

HTTP 是无状态的,因此为了将一个请求与任何其他请求相关联,您需要一种在 HTTP 请求之间存储用户数据的方法。Cookie 和 URL 参数都是在客户端和服务器之间传输数据的合适方法。然而,它们在客户端都是可读的。Session 正好解决了这个问题。您为客户端分配一个 ID,它会使用该 ID 发出所有进一步的请求。与客户端关联的信息存储在与该 ID 链接的服务器上。

我们需要 koa-session,因此使用 -

npm install --save koa-session

我们将放置koa-session中间件。在此示例中,我们将使用 RAM 来存储会话。切勿在生产环境中使用它。会话中间件处理所有事情,即创建会话、设置会话cookie以及在上下文对象中创建会话对象。

每当我们再次从同一个客户端发出请求时,我们都会存储他们的会话信息(假设服务器没有重新启动)。我们可以向这个会话对象添加更多属性。在下面的示例中,我们将为客户端创建一个查看计数器。

var session = require('koa-session');
var koa = require('koa');
var app = koa();

app.keys = ['Shh, its a secret!'];
app.use(session(app));  // Include the session middleware

app.use(function *(){
   var n = this.session.views || 0;
   this.session.views = ++n;
   
   if(n === 1)
      this.body = 'Welcome here for the first time!';
   else
      this.body = "You've visited this page " + n + " times!";
})

app.listen(3000);

上面代码的作用是,当用户访问该站点时,它会为该用户创建一个新会话并分配一个 cookie。下次用户访问时,将检查 cookie,并相应更新 page_view 会话变量。

现在,如果您运行该应用程序并转到localhost:3000,您将收到以下响应。

会话第一

如果您重新访问该页面,页面计数器将会增加。在本例中,页面刷新了 12 次。

第12节

Koa.js - 身份验证

身份验证是一个过程,其中将所提供的凭据与本地操作系统或身份验证服务器内的授权用户信息数据库中的文件进行比较。如果凭据匹配,则该过程完成,并且用户被授予访问授权。

我们将创建一个非常基本的身份验证系统,该系统将使用基本 HTTP 身份验证。这是实施访问控制的最简单的方法,因为它不需要 cookie、会话或其他任何东西。要使用此功能,客户端必须将授权标头与其发出的每个请求一起发送。用户名和密码未加密,而是连接在单个字符串中,如下所示。

username:password

该字符串使用 Base64 编码,并且单词 Basic 放在该值之前。例如,如果您的用户名是 Ayush,密码 India,则字符串“Ayush:India”将按照授权标头中的编码形式发送。

Authorization: Basic QXl1c2g6SW5kaWE=

要在您的 koa 应用程序中实现此功能,您需要 koa-basic-auth 中间件。使用以下命令安装它 -

$ npm install --save koa-basic-auth

现在打开您的 app.js 文件并在其中输入以下代码。

//This is what the authentication would be checked against
var credentials = { name: 'Ayush', pass: 'India' }

var koa = require('koa');
var auth = require('koa-basic-auth');
var _ = require('koa-router')();

var app = koa();

//Error handling middleware
app.use(function *(next){
   try {
      yield next;
   } catch (err) {
      if (401 == err.status) {
         this.status = 401;
         this.set('WWW-Authenticate', 'Basic');
         this.body = 'You have no access here';
      } else {
         throw err;
      }
   }
});

// Set up authentication here as first middleware. 
// This returns an error if user is not authenticated.
_.get('/protected', auth(credentials), function *(){
   this.body = 'You have access to the protected area.';
   yield next;
});

// No authentication middleware present here.
_.get('/unprotected', function*(next){
   this.body = "Anyone can access this area";
   yield next;
});

app.use(_.routes());
app.listen(3000);

我们创建了一个错误处理中间件来处理所有与身份验证相关的错误。然后,我们创建了 2 条路线 -

  • /protected - 仅当用户发送正确的身份验证标头时才能访问此路由。对于所有其他人,它会给出错误。

  • /unprotected - 任何人都可以访问此路由,无论是否经过身份验证。

现在,如果您在没有身份验证标头或使用错误凭据的情况下向 /protected 发送请求,您将收到错误。例如,

$ curl https://localhost:3000/protected

您将收到如下响应:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic
Content-Type: text/plain; charset=utf-8
Content-Length: 28
Date: Sat, 17 Sep 2016 19:05:56 GMT
Connection: keep-alive

Please authenticate yourself

但是,只要有正确的凭据,您就会得到预期的响应。例如,

$ curl -H "Authorization: basic QXl1c2g6SW5kaWE=" https://localhost:3000/protected -i

您将得到如下响应:

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 38
Date: Sat, 17 Sep 2016 19:07:33 GMT
Connection: keep-alive

You have access to the protected area.

/unprotected 路由仍然可供所有人访问。

Koa.js - 压缩

压缩是节省带宽和加快网站速度的简单有效的方法。它仅与现代浏览器兼容,如果您的用户也使用旧版浏览器,则应谨慎使用。

当从服务器发送响应时,如果使用压缩,可以大大缩短加载时间。我们将使用一个名为koa-compress 的中间件来负责文件的压缩以及设置适当的标头。

继续使用以下命令安装中间件 -

$ npm install --save koa-compress

现在在您的 app.js 文件中添加以下代码 -

var koa = require('koa');
var router = require('koa-router');
var app = koa();

var Pug = require('koa-pug');
var pug = new Pug({
   viewPath: './views',
   basedir: './views',
   app: app //Equivalent to app.use(pug)
});

app.use(compress({
   filter: function (content_type) {
      return /text/i.test(content_type)
   },
   threshold: 2048,
   flush: require('zlib').Z_SYNC_FLUSH
}));

var _ = router(); //Instantiate the router

_.get('/', getRoot);

function *getRoot(next){
   this.render('index');
}

app.use(_.routes()); //Use the routes defined using the router
app.listen(3000);

这使我们的压缩中间件就位。过滤选项是检查响应内容类型以决定是否压缩的功能。阈值选项是要压缩的最小响应大小(以字节为单位)。这确保我们不会压缩每一个小响应。

以下是未经压缩的响应。

未压缩

以下是压缩时的类似响应。

压缩的

如果您查看底部的尺寸标签,您可以很好地看到两者之间的差异。当我们压缩文件时,性能提升了 150% 以上。

Koa.js - 缓存

缓存是存储可重用响应以使后续请求更快的术语。每个浏览器都附带了 HTTP 缓存的实现。我们所要做的就是确保每个服务器响应提供正确的 HTTP 标头指令,以指示浏览器何时以及浏览器可以缓存响应多长时间。

以下是在网络应用程序中包含缓存的一些好处 -

  • 您的网络成本降低。如果您的内容已缓存,则您需要为每个后续请求发送较少的内容。

  • 您网站的速度和性能都会提高。

  • 即使您的客户处于离线状态,您的内容也可以使用。

我们将使用 koa-static-cache 中间件在我们的应用程序中实现缓存。使用安装这些中间件 -

$ npm install --save koa-static-cache

转到您的 app.js 文件并向其中添加以下代码。

var koa = require('koa');
var app = koa();

var path = require('path');
var staticCache = require('koa-static-cache');

app.use(staticCache(path.join(__dirname, 'public'), {
   maxAge: 365 * 24 * 60 * 60  //Add these files to caches for a year
}))

app.listen(3000);

koa -static-cache中间件用于在客户端缓存服务器响应。缓存控制标头是根据我们在初始化缓存对象时提供的选项设置的。我们已将此缓存响应的过期时间设置为 1 年。以下是我们在文件缓存之前和之后发送的请求的比较。

在这个文件被缓存之前,返回的状态码是200,这是OK的。响应标头包含有关要缓存的内容的多个信息,并且还给出了内容的ETag 。

缓存之前

下次发送请求时,它会与 ETtag 一起发送。由于我们的内容在服务器上没有更改,因此其相应的 ETag 也保持不变,并且客户端被告知其本地的副本与服务器提供的内容是最新的,并且应该使用本地副本而不是请求再次。

缓存后

注意- 要使任何缓存文件无效,您只需更改其文件名并更新其引用。这将确保您有一个新文件要发送到客户端,并且客户端无法从缓存中加载它。

Koa.js - 数据库

我们正在收到请求,但不会将它们存储在任何地方。我们需要一个数据库来存储数据。我们将使用一个名为MongoDB的著名 NoSQL 数据库。要安装并阅读有关 Mongo 的信息,请访问此链接。

为了将 Mongo 与 Koa 结合使用,我们需要一个节点的客户端 API。我们有多种选择,但在本教程中我们将坚持使用mongoose。Mongoose 用于Node for MongoDB 中的文档建模。文档建模意味着,我们将创建一个模型(很像面向文档编程中的),然后我们将使用这个模型生成文档(就像我们在 OOP 中创建类的文档一样)。我们所有的处理都将在这些“文档”上完成,最后,我们将这些文档写入我们的数据库中。

设置猫鼬

现在我们已经安装了 Mongo,让我们安装 mongoose,就像我们安装其他节点包一样。

$ npm install --save mongoose

在开始使用 mongoose 之前,我们必须使用 Mongo shell 创建一个数据库。要创建新数据库,请打开终端并输入“mongo”。Mongo shell 将启动,输入以下内容。

use my_db

将为您创建一个新数据库。每当您打开 Mongo shell 时,它都会默认为“test”数据库,您必须使用与上面相同的命令更改为数据库。

要使用 mongoose,我们将在 app.js 文件中需要它,然后连接到在 mongodb://localhost 上运行的 mongod 服务

var koa = require('koa');
var _ = require('koa-router')();
var app = koa();

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/my_db');

app.use(_.routes());
app.listen(3000);

现在我们的应用程序已连接到数据库,让我们创建一个新模型。该模型将充当我们数据库中的集合。要创建新模型,请在定义任何路由之前使用以下代码。

var koa = require('koa');
var _ = require('koa-router')();
var app = koa();

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/my_db');

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});

var Person = mongoose.model("Person", personSchema);

app.use(_.routes());
app.listen(3000);

上面的代码定义了一个人的模式,并用于创建一个 mongoose 模型Person

保存文档

现在我们将创建一个新的 html 表单,它将获取一个人的详细信息并将其保存到我们的数据库中。要创建表单,请在views 目录中创建一个名为person.pug 的新视图文件,其中包含以下内容。

html
   head
      title Person
   body
      form(action = "/person", method = "POST")
         div
            label(for = "name") Name: 
            input(name = "name")
         br
         div
            label(for = "age") Age: 
            input(name = "age")
         br
         div
            label(for = "nationality") Nationality: 
            input(name = "nationality")
         br
         button(type = "submit") Create new person

还要在index.js 中添加一个新的获取路由来呈现此文档。

var koa = require('koa');
var _ = require('koa-router')();
var app = koa();

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/my_db');

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});

var Person = mongoose.model("Person", personSchema);

_.get('/person', getPerson);

function *getPerson(next){
   this.render('person');
   yield next;
}

app.use(_.routes());
app.listen(3000);

转到 localhost:3000/person 检查我们的表单是否显示正确。请注意,这只是 UI,尚未运行。这就是我们的表单的样子。

猫鼬创建

现在,我们将在“/person”处定义一个后路由处理程序来处理此请求。

var koa = require('koa');
var _ = require('koa-router')();
var app = koa();

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/my_db');

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});

var Person = mongoose.model("Person", personSchema);

_.post('/per