node deno_如何在Deno和Oak中创建Todo API
node deno
I am a JavaScript/Node developer who secretly likes (actually, loves and adores) Deno. I have been a huge fan of Deno ever since it was announced and I've been wanting to play with it.
我是一个JavaScript / Node开发人员,他暗中喜欢(实际上是爱和崇拜)Deno。 自从它发布以来,我一直是Deno的忠实粉丝,而且我一直想与它一起玩。
This tutorial focuses on creating a set of REST APIs for a Todo application. Keep in mind that I did not touch on the database here – I will cover that in another article.
本教程重点介绍为Todo应用程序创建一组REST API。 请记住,我在这里没有涉及数据库-我将在另一篇文章中介绍 。
At any point if you feel lost or want to check a reference, here is the entire source code of this tutorial: Chapter 1: Oak.
在任何时候,如果您感到迷路或想要检查参考,这里都是本教程的全部源代码: 第1章:Oak 。
我们将介绍的内容 (Things we will cover)
- Create a basic server 创建一个基本服务器
- Create 5 APIs (routes/controller) 创建5个API(路由/控制器)
- Create a middleware to log API requests as they are made in the console 创建一个中间件来记录在控制台中发出的API请求
- Create a not found (404) middleware when the user tries to access an unknown API 用户尝试访问未知API时,创建未找到(404)中间件
我们需要什么 (What will we need)
- An installed version of Deno (don't worry I'll walk you through it) Deno的已安装版本(不用担心,我会带您逐步了解)
- A tiny bit of knowledge of Typescript 一小部分打字稿知识
- Would be awesome if you have worked with Node/Express before (don't worry if you haven't — this tutorial is very basic) 如果您以前使用过Node / Express,那就太棒了(如果您还没有使用过它,请不要担心-本教程非常基础)
让我们开始吧 (Let's get started)
First things first let's install Deno. I am on a Mac computer so I am using brew. Simply open your terminal and type:
首先,让我们安装Deno。 我在Mac电脑上,所以我正在使用brew。 只需打开终端并输入:
$ brew install deno
But if you are using a different operating system, just head over to deno.land installation. They have a lot of ways you can easily install it on your machine.
但是,如果您使用的是其他操作系统,则直接转到deno.land安装 。 他们有很多方法可以轻松地将其安装在计算机上。
Once you have it installed, close the terminal, open a new one, and type:
安装完毕后,关闭终端,打开一个新终端,然后键入:
$ deno --version
It should output something like this:
它应该输出如下内容:
Awesome! With this we are almost done with 10% of this tutorial.
太棒了! 这样,我们几乎完成了本教程的10%。
Let's move ahead and create the backend API for our Todo app.
让我们继续前进,为我们的Todo应用程序创建后端API。
设置项目 (Setting up the project)
Before you move on, here is the entire source code of this tutorial: Chapter 1: Oak.
在继续之前,这里是本教程的全部源代码: 第1章:Oak 。
Let's get started:
让我们开始吧:
-
Create a new folder and call it chapter_1:oak (but you can call it anything you want)
创建一个新文件夹,并将其命名为Chapter_1:oak (但是您可以根据需要随意命名)
-
Once you create a folder simply
cd
into your new project. Create a file called server.ts and write the following code in it:创建文件夹后,只需
cd
进入新项目。 创建一个名为server.ts的文件 并在其中编写以下代码:
import { Application } from "https://deno.land/x/oak/mod.ts";
const app = new Application();
const port: number = 8080;
console.log('running on port ', port);
await app.listen({ port });
Let's run this file. Open your terminal and in your project root folder type:
让我们运行这个文件。 打开终端,然后在项目根文件夹中输入:
$ deno run --allow-net server.ts
I will talk about what the --allow-net
flag does, but for now just bear with me ????.
我将讨论--allow-net
标志的作用,但现在请耐心等待????。
You should get something like this:
您应该得到这样的内容:
What we have done so far is create a server which listens on port 8080. It doesn't do much right now besides being able to run on port 8080.
到目前为止,我们所做的是创建一个侦听端口8080的服务器。除了能够在端口8080上运行之外,它现在并没有做其他事情。
If you have used JavaScript before, one thing you might have noticed is we are importing packages in a different way. We have to do something like:
如果您以前使用过JavaScript,那么您可能已经注意到的一件事是,我们以另一种方式导入软件包。 我们必须做类似的事情:
import { Application } from "https://deno.land/x/oak/mod.ts";
When you run deno run ---allow-net <file_name>
in your terminal, Deno will look at all your imports and install them locally in your machine if they are not there.
在终端中运行deno run ---allow-net <file_name>
时,Deno将查看所有导入,如果不存在,则将它们本地安装在计算机中。
The first time you run this it will go to this URL https://deno.land/x/oak/mod.ts
and install the oak
package. Oak is basically a Deno framework for writing API's. It will put it somewhere locally in your cache.
首次运行时,它将转到该URL https://deno.land/x/oak/mod.ts
并安装oak
软件包。 Oak基本上是一个用于编写API的Deno框架。 它将把它放在您缓存的本地位置。
In the next line we do this:
在下一行中,我们这样做:
const app = new Application();
This creates a new instance of our application, and it will be the basis of everything as you progress further in this tutorial. You can add routes to the application instance, attach middleware like API logging, write a 404 not found, and so on.
这将创建我们的应用程序的新实例,它将作为您在本教程中进一步发展的一切基础。 您可以将路由添加到应用程序实例,附加API日志记录之类的中间件,编写未找到的404,等等。
Then we write:
然后我们写:
const port: number = 8080;
// const port = 8080; // => can also be written like this
Both are the same and do the same thing. The only difference is writing const port: number = 8080
tells Typescript that port
variable is of type number.
两者是相同的,并且做相同的事情。 唯一的区别是编写const port: number = 8080
告诉Typescript port
变量的类型为number。
If you were to write const port: number = "8080"
, this would throw an error in your terminal, as port is of type number
. But we are trying to assign it a string
of value "8080".
如果您要编写const port: number = "8080"
,这将在终端中引发错误,因为port的类型为number
。 但是,我们正在尝试为其分配值“ 8080”的string
。
If you want to learn more about different types of types (pun intended) check out this very easy and basic guide on Basic types by Typescript. Just give it a quick glance for 2-3 minutes and head back here.
如果您想了解更多有关不同类型的类型的信息(请使用双关语),请阅读有关Typetype的Basic类型的非常简单和基本的指南。 只需快速浏览2-3分钟,然后回到这里。
And in the end we have:
最后,我们有:
console.log('running on port ', port);
await app.listen({ port });
We simply console here the port number and tell Deno to listen to the port, which is 8080.
我们只需在此处控制台端口号,并告诉Deno监听8080端口。
It isn't doing much right now. Let's make it do something basic like show a JSON message in your browser when you go to http:localhost:8080.
现在没有做太多事情。 让我们做一些基本的事情,例如当您访问http:localhost:8080时在浏览器中显示JSON消息。
Add the following to your server.ts file:
将以下内容添加到您的server.ts文件中:
import { Application, Router } from "https://deno.land/x/oak/mod.ts";
const app = new Application();
const port: number = 8080;
const router = new Router();
router.get("/", ({ response }: { response: any }) => {
response.body = {
message: "hello world",
};
});
app.use(router.routes());
app.use(router.allowedMethods());
console.log('running on port ', port);
await app.listen({ port });
The new thing added here is that we are now also importing Router
along with Application
from oak
in line 1.
此处添加的新内容是,我们现在还在第1行中从oak
导入Router
和Application
。
Next what we do is:
接下来,我们要做的是:
const router = new Router();
router.get("/", ({ response }: { response: any }) => {
response.body = {
message: "hello world",
};
});
app.use(router.routes());
app.use(router.allowedMethods());
We create a new router instance by doing const router = new Router()
and then we create a new route called /
which is of type get
.
我们通过执行const router = new Router()
创建一个新的路由器实例,然后创建一个名为/
的新路由,其类型为get
。
Let's break this down:
让我们分解一下:
router.get("/", ({ response }: { response: any }) => {
response.body = {
message: "hello world",
};
});
router.get
takes 2 parameters. The first is route which we have set to /
and the second is function. The function itself takes an argument which is an object. What I am doing here is destructuring the object and getting only response
.
router.get
需要2个参数。 第一个是我们设置为/
路由,第二个是函数。 该函数本身带有一个作为对象的参数。 我在这里所做的是破坏对象并仅获得response
。
Next I am type checking response
similar to how I did const port: number = 8080;
. All I am doing is { response }: { response: any }
which is telling TypeScript here that the response
which I have destructed can be of type any
.
接下来,我将进行类型检查response
类似于我对const port: number = 8080;
所做的操作const port: number = 8080;
。 我正在做的只是{ response }: { response: any }
这告诉TypeScript在这里我破坏的response
可以是any
类型。
any
helps you avoid type checking in TypeScript. You can read more about it here.
any
帮助您避免在TypeScript中进行类型检查的方法。 您可以在此处了解更多信息。
Then all I am doing is taking that response
object and setting response.body.message = "hello world";
.
然后,我要做的就是获取该response
对象并设置response.body.message = "hello world";
。
response.body = {
message: "hello world",
};
Last but not least, we just add these two lines:
最后但并非最不重要的一点是,我们仅添加以下两行:
app.use(router.routes());
app.use(router.allowedMethods());
This tells Deno to include all routes by our router (currently we only have one) and the next line tells Deno to allow all methods for this route(s) like GET, POST, PUT, DELETE
.
这告诉Deno包括我们路由器的所有路由(当前只有一个),而下一行告诉Deno允许该路由的所有方法,例如GET, POST, PUT, DELETE
。
And now we are done. ✅ Let's run this and see what we have:
现在我们完成了。 this让我们运行它,看看有什么:
$ deno run --allow-net server.ts
The ---allow-net
property tells Deno that this app gives the user the permission to access its content via the port opened up.
---allow-net
属性告诉Deno,此应用程序授予用户通过打开的端口访问其内容的权限。
Now open your favorite browser and go to http://localhost:8080
. You will see something like this:
现在打开您喜欢的浏览器,然后转到http://localhost:8080
。 您将看到如下内容:
Honestly the hardest part is done. Conceptually we are 60% there.
老实说,最难的部分已经完成。 从概念上讲,我们在那里占60%。
Awesome.
太棒了
Just one last thing before we start with our Todo API. Let's replace:
在开始使用Todo API之前,只有最后一件事。 让我们替换:
console.log('running on port ', port);
await app.listen({ port });
with:
与:
app.addEventListener("listen", ({ secure, hostname, port }) => {
const protocol = secure ? "https://" : "http://";
const url = `${protocol}${hostname ?? "localhost"}:${port}`;
console.log(`Listening on: ${port}`);
});
await app.listen({ port });
The code we had before was not very accurate, because we were simply console logging a message and then waiting for the app to start listening on a port.
我们之前的代码不是很准确,因为我们只是在控制台上记录一条消息,然后等待应用开始在端口上监听。
With the later version we wait for the app to start listening on port
and we can listen by adding an event listener to our app
instance with the following: app.addEventListener("listen", ({ secure, hostname, port }) => {}
.
在更高版本中,我们等待应用程序开始在port
侦听,并且可以通过以下方式向我们的app
实例添加事件侦听器: app .addEventListener ("listen", ({ secure, hostname, port }) => {}
。
The first param is the event we want to listen for (which is listen
????) and then the second param is an object which we destruct to { secure, hostname, port }
. Secure is a boolean, hostname is a string, and port is a number.
第一参数是我们要监听(是事件listen
????),然后将第二参数是我们破坏到对象{ secure, hostname, port }
。 安全是布尔值,主机名是字符串,端口是数字。
Now when we start our app, it will only console the message once the app actually starts listening on port.
现在,当我们启动我们的应用程序时,只有在该应用程序实际开始监听端口时,它才会控制台消息。
We can just go one step ahead and make it more colorful. Let's add a new module to the top of the file in server.ts
:
我们可以向前迈出一步,使其更加丰富多彩。 让我们在server.ts
文件的顶部添加一个新模块:
import { green, yellow } from "https://deno.land/aaa@qq.com/fmt/colors.ts";
And then inside our event listener method we can replace:
然后在事件监听器方法中,我们可以替换为:
console.log(`Listening on: ${port}`);
with:
与:
console.log(`${yellow("Listening on:")} ${green(url)}`);
Now when we do:
现在,当我们这样做时:
$ deno run --allow-net server.ts
it will show this in our console:
它将在我们的控制台中显示:
If you get stuck anywhere you can simply go to the source code of this tutorial here.
如果您遇到任何地方,你可以简单地去本教程的源代码在这里 。
Let's create our Todo API's routes next.
接下来,让我们创建Todo API的路由。
-
Create a new folder in your root folder called
routes
and inside that folder create a file calledtodo.ts
在你的根文件夹,名为创建一个新的文件夹
routes
和文件夹中创建一个名为todo.ts
-
At the same time in your root folder create a new folder called
controllers
and inside that folder create a file calledtodo.ts
同时在您的根文件夹中创建一个名为
controllers
的新文件夹,并在该文件夹中创建一个名为todo.ts
的文件。
Let's first touch the controllers/todo.ts
file:
让我们首先触摸controllers/todo.ts
文件:
export default {
getAllTodos: () => {},
createTodo: async () => {},
getTodoById: () => {},
updateTodoById: async () => {},
deleteTodoById: () => {},
};
We are simply exporting an object here with some named functions which are empty (for now).
我们只是在这里导出一个带有一些命名函数的对象,这些函数是空的(目前)。
Next go inside your file routes/todo.ts
and type this:
接下来进入文件routes/todo.ts
并输入以下内容:
import { Router } from "https://deno.land/x/oak/mod.ts";
const router = new Router();
// controller
import todoController from "../controllers/todo.ts";
router
.get("/todos", todoController.getAllTodos)
.post("/todos", todoController.createTodo)
.get("/todos/:id", todoController.getTodoById)
.put("/todos/:id", todoController.updateTodoById)
.delete("/todos/:id", todoController.deleteTodoById);
export default router;
This might look familiar to people who have worked with Node and Express.
对于使用Node and Express的人来说,这可能看起来很熟悉。
All we are doing here is importing Route
from oak
and then setting up a new instance of Router by doing const router = new Router();
.
我们在这里所做的就是从oak
导入Route
,然后通过执行const router = new Router();
设置一个新的Router实例const router = new Router();
。
Next we import our controllers by doing:
接下来,我们通过执行以下操作导入控制器:
import todoController from "../controllers/todo.ts";
One thing to notice here in Deno is every time we import a local file in our Deno project we have to provide the file extension. This is because Deno doesn't know whether the file being imported is a .js
or .ts
file.
在Deno中要注意的一件事是,每次在Deno项目中导入本地文件时,我们都必须提供文件扩展名。 这是因为Deno不知道导入的文件是.js
还是.ts
文件。
Moving forward we simply set all of our routes according to REST conventions:
展望未来,我们只需根据REST约定设置所有路由:
router
.get("/todos", todoController.getAllTodos)
.post("/todos", todoController.createTodo)
.get("/todos/:id", todoController.getTodoById)
.put("/todos/:id", todoController.updateTodoById)
.delete("/todos/:id", todoController.deleteTodoById);
The code above will translate to our API definition like this:
上面的代码将转换为我们的API定义,如下所示:
TYPE | API ROUTE | |||
---|---|---|---|---|
GET | /todos | |||
GET | /todos/:id | |||
POST | /todos | |||
PUT | /todos/:id | |||
DELETE | /todos/:id |
类型 | API路由 | |||
---|---|---|---|---|
得到 | / todos | |||
得到 | / todos /:id | |||
开机自检 | / todos | |||
放 | / todos /:id | |||
删除 | / todos /:id |
and at the end we simply export our router by doing export default router;
.
最后,我们只需export default router;
即可导出export default router;
。
We are done with creating our routes structure. (Now, each route doesn't do anything because our controllers are empty, we will add functionality to them in a bit.)
我们已经完成了创建路线结构的工作。 (现在,由于我们的控制器是空的,所以每条路线都不会做任何事情,我们将向其中添加功能。)
Here's the last piece of the puzzle before we start adding functionality to each route controller. We need to attach this router
to our app
instance.
这是我们开始向每个路由控制器添加功能之前的最后一个难题。 我们需要将此router
附加到我们的app
实例。
So head over to server.ts
file and do the following:
因此,转到server.ts
文件并执行以下操作:
- Add this to the very top: 将此添加到最顶部:
// routes
import todoRouter from "./routes/todo.ts";
- Remove this piece of code: 删除这段代码:
const router = new Router();
router.get("/", ({ response }: { response: any }) => {
response.body = {
message: "hello world",
};
});
app.use(router.routes());
app.use(router.allowedMethods());
- Replace it with: 替换为:
app.use(todoRouter.routes());
app.use(todoRouter.allowedMethods());
This is it – we are done. Your server.ts
file should look like this now:
就这样–我们完成了。 您的server.ts
文件现在应如下所示:
import { Application } from "https://deno.land/x/oak/mod.ts";
import { green, yellow } from "https://deno.land/aaa@qq.com/fmt/colors.ts";
// routes
import todoRouter from "./routes/todo.ts";
const app = new Application();
const port: number = 8080;
app.use(todoRouter.routes());
app.use(todoRouter.allowedMethods());
app.addEventListener("listen", ({ secure, hostname, port }) => {
const protocol = secure ? "https://" : "http://";
const url = `${protocol}${hostname ?? "localhost"}:${port}`;
console.log(
`${yellow("Listening on:")} ${green(url)}`,
);
});
await app.listen({ port });
If you got stuck anywhere while following this, simple head over to the source code of this tutorial here.
如果您在执行此操作时遇到任何困难,请在此处简单地转到本教程的源代码。
Awesome, now we have our routes with no functionality at the moment. So let's add that functionality in our controllers.
太棒了,现在我们的路线目前没有任何功能。 因此,让我们在控制器中添加该功能。
But before we do that we have to create 2 more (tiny) files.
但是在此之前,我们必须再创建2个(小)文件。
-
In your root folder create a new folder called
interfaces
and inside that folder create a file calledTodo.ts
(make sure Todo is capitalized, as it won't give any syntax error here if you don't – these are just conventions.)在您的根文件夹中,创建一个名为
interfaces
的新文件夹,并在该文件夹中创建一个名为Todo.ts
的文件(请确保Todo为大写,因为如果您不这样做,它将不会在此处出现任何语法错误-这只是约定。) -
Also in your root folder create a new folder called
stubs
and inside that folder create a file calledtodos.ts
同样在您的根文件夹中,创建一个名为
stubs
的新文件夹,并在该文件夹中创建一个名为todos.ts
的文件。
Let's create an interface in our interfaces/Todo.ts
file. Simply add the following code:
让我们在interfaces/Todo.ts
文件中创建一个接口。 只需添加以下代码:
export default interface Todo {
id: string,
todo: string,
isCompleted: boolean,
}
What is an interface?
什么是接口?
One of the core things in TypeScript is checking the shape that value has. Similar to const port: number = 8080
or { response }: { response : any }
, we can also type check an object.
TypeScript的核心内容之一就是检查值的形状。 与const port: number = 8080
或{ response }: { response : any }
类似,我们也可以键入check一个对象。
In TypeScript, interfaces fill the role of naming these types, and are a powerful way of defining contracts within your code as well as contracts with code outside of your project.
在TypeScript中,接口充当命名这些类型的角色,并且是定义代码内 契约以及项目外代码契约的有效方法。
Here is an another example of an interface:
这是接口的另一个示例:
// We have an interface
interface LabeledValue {
label: string;
}
// the arg passed to this function labeledObj is
// of type LabeledValue (interface)
function printLabel(labeledObj: LabeledValue) {
console.log(labeledObj.label);
}
let myObj = {label: "Size 10 Object"};
printLabel(myObj);
Hopefully this example gives you a bit more insight into interfaces. If you want more detailed information check out the docs on interfaces here.
希望本示例可以使您对界面有更多的了解。 如果您想了解更多详细信息,请在此处查看有关接口的文档。
Now that our interface is ready, let's mock some data (since we don't have an actual database for this tutorial).
现在我们的界面已经准备就绪,让我们模拟一些数据(因为我们没有本教程的实际数据库)。
Let's create a mock list of todos first in our stubs/todos.ts
file. Simply add the following:
让我们首先在stubs/todos.ts
文件中创建一个stubs/todos.ts
的模拟列表。 只需添加以下内容:
import { v4 } from "https://deno.land/std/uuid/mod.ts";
// interface
import Todo from '../interfaces/Todo.ts';
let todos: Todo[] = [
{
id: v4.generate(),
todo: 'walk dog',
isCompleted: true,
},
{
id: v4.generate(),
todo: 'eat food',
isCompleted: false,
},
];
export default todos;
-
Two things to notice here: we add a new package and use its method
v4
by doingimport { v4 } from "https://deno.land/std/uuid/mod.ts";
. Then every time we usev4.generate()
it will create a new random string ofid
.这里需要注意两件事:我们通过
import { v4 } from "https://deno.land/std/uuid/mod.ts";
添加一个新包并使用其方法v4
import { v4 } from "https://deno.land/std/uuid/mod.ts";
。 然后,每次我们使用v4.generate()
,都会创建一个新的随机字符串id
。Two things to notice here: we add a new package and use its method
v4
by doingimport { v4 } from "https://deno.land/std/uuid/mod.ts";
. Then every time we usev4.generate()
it will create a new random string ofid
.这里需要注意两件事:我们通过
import { v4 } from "https://deno.land/std/uuid/mod.ts";
添加新包并使用其方法v4
import { v4 } from "https://deno.land/std/uuid/mod.ts";
。 然后,每次我们使用v4.generate()
,都会创建一个新的随机字符串id
。The
的
id
can not be anumber
, only astring
because in ourTodo
interface we have definedid
as a string.id
不能是number
,只能是string
因为在Todo
界面中我们将id
定义为字符串。 -
The other thing to focus on here is
let todos: Todo[] = []
. This basically tells Deno that our todos array is of typeTodo
(which is awesome, our compiler now automagically knows that each item in our array can only have{id: string, todo: string & isCompleted: boolean}
it will not accept any other key).这里要重点关注的另一件事是
let todos : Todo [] = []
let todos : Todo [] = []
let todos : Todo [] = []
。 这基本上告诉了Deno我们的todos数组是Todo
类型的(这太了不起了,我们的编译器现在自动地知道我们数组中的每个项目只能有{ id : string , todo : string & isCompleted : boolean }
,它将不接受其他任何内容键)。
If you want to learn more about interfaces
in TypeScript check out this amazing detailed documentation on interfaces here.
如果您想了解有关TypeScript interfaces
更多信息,请在此处查看有关interfaces
惊人详细文档。
Awesome. If you have come this far, give yourself a pat on the back. Good job everyone.
太棒了 如果您走了这么远,请拍一下自己的背。 大家好
让我们来研究我们的控制器 (Let's work on our controllers)
In your file controllers/todo.ts
:
在您的文件controllers/todo.ts
:
export default {
getAllTodos: () => {},
createTodo: async () => {},
getTodoById: () => {},
updateTodoById: async () => {},
deleteTodoById: () => {},
};
Let's write the controller for getAllTodos
:
让我们为getAllTodos
编写控制器:
// stubs
import todos from "../stubs/todos.ts";
export default {
/**
* @description Get all todos
* @route GET /todos
*/
getAllTodos: ({ response }: { response: any }) => {
response.status = 200;
response.body = {
success: true,
data: todos,
};
},
createTodo: async () => {},
getTodoById: () => {},
updateTodoById: async () => {},
deleteTodoById: () => {},
};
Before I begin on this block of code, let me explain that every controller has an argument – let's call it context
.
在开始这段代码之前,让我解释一下每个控制器都有一个参数–我们称它为context
。
So we can deconstruct getAllTodos: (context) => {}
to:
因此,我们可以将getAllTodos : (context) => {}
解构为:
getAllTodos: ({ request, response, params }) => {}
And since we are using typescript
we have to add type checking to all of these variables:
由于我们使用的是typescript
我们必须为所有这些变量添加类型检查:
getAllTodos: (
{ request, response, params }: {
request: any,
response: any,
params: { id: string },
},
) => {}
So we have added type checks to all 3 { request, response, params }
因此,我们为所有3个{ request, response, params }
添加了类型检查
-
request
is what the user sends us (information like headers and JSON data)request
是用户向我们发送的内容(标题和JSON数据之类的信息) -
response
is what we send the user back in the API responseresponse
就是我们在API响应中发回用户的内容 -
params
is what we define in our router routes, that is:params
是我们在路由器路由中定义的,即:
.get("/todos/:id", ({ params}: { params: { id: string } }) => {})
So the :id
in /todos/:id
is the param. Params are a way to get information from the URL. In this example we know that we have an /:id
. So when the user tries to access this API (that is, /todos/756
) 756 is basically the :id param. Since it is in the URL we know it is of type string
.
所以:id
在/todos/:id
是帕拉姆。 参数是从URL获取信息的一种方法。 在此示例中,我们知道我们有一个/:id
。 因此,当用户尝试访问此API(即/todos/756
)时, 756基本上是:id参数。 由于它在URL中,因此我们知道它的类型为string
。
Now that we have our basic definitions defined let's get back to our todos controller:
现在我们定义了基本定义,让我们回到我们的todos控制器:
// stubs
import todos from "../stubs/todos.ts";
export default {
/**
* @description Get all todos
* @route GET /todos
*/
getAllTodos: ({ response }: { response: any }) => {
response.status = 200;
response.body = {
success: true,
data: todos,
};
},
createTodo: async () => {},
getTodoById: () => {},
updateTodoById: async () => {},
deleteTodoById: () => {},
};
For getAllTodos
we only need response
. If you remember, response
is what is needed to send data back to the user.
对于getAllTodos
我们只需要response
。 如果您还记得,那么response
就是将数据发送回用户所需要的。
For people coming from a Node and Express background, one big thing that is different here is that we don't need to return
the response object. Deno does this for us automatically.
对于来自Node和Express背景的人们,这里的一大不同是我们不需要return
响应对象。 Deno会自动为我们执行此操作。
All we have to do is set response.status
which in this case is 200
.
我们要做的就是设置response.status
,在这种情况下为200
。
More on response statuses here.
有关响应状态的更多信息,请点击此处 。
The other thing we set is the response.body
which in this case is an object:
我们设置的另一件事是response.body
,在这种情况下,它是一个对象:
{
success: true,
data: todos
}
I will go ahead and run my server:
我将继续运行服务器:
$ deno run --allow-net server.ts
Revision: The 修订:---allow-net
property tells Deno that this app gives the user permission to access its content via the port opened up.---allow-net
属性告诉Deno,此应用程序授予用户权限通过打开的端口访问其内容。
Once your server is running, you can access the GET /todos
API. I am using postman
which is a Google Chrome extension and can be downloaded here.
服务器运行后,就可以访问GET /todos
API。 我正在使用postman
,这是Google Chrome扩展程序,可以在这里下载。
You can use whatever rest client you like. I like using postman
because I think it is very easy.
您可以使用任何喜欢的休息客户。 我喜欢使用postman
因为我认为这很容易。
In Postman, open up a new tab. Set the request to type GET
and in the URL
bar type http://localhost:8080/todos
. Hit Send
and this is what you see:
在邮递员中,打开一个新选项卡。 将请求设置为键入GET
然后在URL
栏中键入http://localhost:8080/todos
。 点击Send
,您将看到以下内容:
Cool! 1 API done, 4 more to go. ????????
凉! 完成1个API,还有4个API。 ????????
If you feel stuck anywhere just have sneak peak at the source code directly here.
Let's move on to our next controller:
让我们继续下一个控制器:
import { v4 } from "https://deno.land/std/uuid/mod.ts";
// interfaces
import Todo from "../interfaces/Todo.ts";
// stubs
import todos from "../stubs/todos.ts";
export default {
getAllTodos: () => {},
/**
* @description Add a new todo
* @route POST /todos
*/
createTodo: async (
{ request, response }: { request: any; response: any },
) => {
const body = await request.body();
if (!request.hasBody) {
response.status = 400;
response.body = {
success: false,
message: "No data provided",
};
return;
}
// if everything is fine then perform
// operation and return todos with the
// new data added.
let newTodo: Todo = {
id: v4.generate(),
todo: body.value.todo,
isCompleted: false,
};
let data = [...todos, newTodo];
response.body = {
success: true,
data,
};
},
getTodoById: () => {},
updateTodoById: async () => {},
deleteTodoById: () => {},
};
Since we are going to be adding a new Todo to our list, I have imported 2 modules in the controller file.
由于我们要向列表中添加一个新的Todo,因此我在控制器文件中导入了2个模块。
-
import { v4 } from `https://deno.land/std/uuid/mod.ts`;
this will be used to create a new unique one for the todo being createdimport { v4 } from ` https://deno.land/std/uuid/mod.ts `;
这将用于为要创建的待办事项创建一个新的唯一的 -
import Todo from "../interfaces/Todo.ts";
this will be used to ensure that the new todo that is being created follows the same structure.import Todo from "../interfaces/Todo.ts";
这将用于确保创建的新待办事项遵循相同的结构。
Our createTodo
controller is async
meaning there are some promises used inside the controller.
我们的createTodo
控制器是async
意味着在控制器内部使用了一些promise。
Let's break it into smaller parts:
让我们将其分成更小的部分:
const body = await request.body();
if (!request.hasBody) {
response.status = 400;
response.body = {
success: false,
message: "No data provided",
};
return;
}
First we get the content of the JSON body that the user has sent us. Then we use oak's
built-in method called request.hasBody
to check if the user has even sent any content. If not then we can do if (!request.hasBody) {}
inside this if
block.
首先,我们获取用户发送给我们的JSON正文的内容。 然后,我们使用oak's
内置方法request.hasBody
来检查用户是否发送了任何内容。 如果没有,那么我们可以做if (!request . hasBody) {}
这里面if
块。
We set the status to 400
(400 means that the user did something they were not suppose to do) and the body is set to {success: false, message: "no data provided }
. Then we simple add return;
to ensure that no further code below is executed.
我们将状态设置为400
(400表示用户做了他们不应该做的事情),并且正文设置为{success: false, message: "no data provided }
。然后我们简单地添加return;
以确保没有下面的代码将被执行。
Next we do this:
接下来,我们这样做:
// if everything is fine then perform
// operation and return todos with the
// new data added.
let newTodo: Todo = {
id: v4.generate(),
todo: body.value.todo,
isCompleted: false,
};
let data = [...todos, newTodo];
response.body = {
success: true,
data,
};
We create a new todo by doing this:
为此,我们创建了一个新的待办事项:
let newTodo: Todo = {
id: v4.generate(),
todo: body.value.todo,
isCompleted: false,
};
let newTodo: Todo = {}
ensures that newTodo
follows the same structure as the rest of the todos. We then assign a random id by using v4.generate()
, set todo to body.value.todo
and isCompleted
to false
.
let newTodo: Todo = {}
确保newTodo
遵循与其余let newTodo: Todo = {}
相同的结构。 然后,我们使用v4.generate()
分配一个随机ID,将todo设置为body.value.todo
并将isCompleted
为false
。
The thing to notice here is all the data the user sends us we can access from body.value
in oak
.
事情到这里通知是所有的用户送给我们的数据,我们可以从访问body.value
在oak
。
Next we do the following:
接下来,我们执行以下操作:
let data = [...todos, newTodo];
response.body = {
success: true,
data,
};
Append the newTodo
to our current list of todos and simply set the body to {success: true & data: data
.
将newTodo
附加到我们当前的newTodo
列表中,然后将正文设置为{success: true & data: data
。
And we are done ✅ with this controller as well.
并且我们也完成了此控制器的操作。
Let's restart our server:
让我们重新启动服务器:
$ deno run --allow-net server.ts
In my postman, I open up a new tab. Set the request to POST
type and in the URL
bar type http://localhost:8080/todos
. Then hit Send
and this is what you see:
在邮递员中,我打开一个新选项卡。 将请求设置为POST
类型,然后在URL
栏中键入http://localhost:8080/todos
。 然后点击Send
,您将看到以下内容:
Then I send some content in the body of the request payload and try again:
然后,我在请求有效内容的正文中发送一些内容,然后重试:
Cool, we can see that our API is working as expected.
太酷了,我们可以看到我们的API可以正常工作。
Two APIs down, three more to go.
减少了两个API,还有三个。
We are almost there. Most of the hard work is done. ☺️ ???? ???? ????
我们就快到了。 大部分的辛苦工作已经完成。 ☺️????
Let's move on to our third API:
让我们继续第三个API:
import { v4 } from "https://deno.land/std/uuid/mod.ts";
// interfaces
import Todo from "../interfaces/Todo.ts";
// stubs
import todos from "../stubs/todos.ts";
export default {
getAllTodos: () => {},
createTodo: async () => {},
/**
* @description Get todo by id
* @route GET todos/:id
*/
getTodoById: (
{ params, response }: { params: { id: string }; response: any },
) => {
const todo: Todo | undefined = todos.find((t) => {
return t.id === params.id;
});
if (!todo) {
response.status = 404;
response.body = {
success: false,
message: "No todo found",
};
return;
}
// If todo is found
response.status = 200;
response.body = {
success: true,
data: todo,
};
},
updateTodoById: async () => {},
deleteTodoById: () => {},
};
Let's talk about our controller for GET todos/:id
. This will get us a todo by ID.
让我们谈谈GET todos/:id
控制器。 这将使我们获得ID的待办事项。
Let's break this down into smaller parts and discuss it:
让我们将其分解为较小的部分并进行讨论:
const todo: Todo | undefined = todos.find((t) => t.id === params.id);
if (!todo) {
response.status = 404;
response.body = {
success: false,
message: "No todo found",
};
return;
}
In the first part we set a new const todo
and set its type to either Todo
or undefined
. So todo
will either be an object with the Todo
interface shape or it will be undefined
– it can not be anything else.
在第一部分中,我们设置了一个新的const todo
并将其类型设置为Todo
或undefined
。 因此, todo
将是具有Todo
界面形状的对象,或者将是undefined
-它不能是其他任何东西。
We then todos.find((t) => t.id === params.id);
use Array.find() to find the todo
with the id provided in params.id
. If it matches we get a Todo
with shape todo
, otherwise undefined
.
然后我们todos.find (( t ) => t.id === params.id );
todos.find (( t ) => t.id === params.id );
使用Array.find()查找具有params.id
提供的ID的todo
。 如果匹配,我们将得到一个具有Todo
形状的todo
,否则为undefined
。
If todo
is undefined, it means that this if
block will run:
如果todo
未定义,则意味着此if
块将运行:
if (!todo) {
response.status = 404;
response.body = {
success: false,
message: "No todo found",
};
return;
}
Here we simply set the status to 404
which means not found
along with our standard failure response or { status, message }
在这里,我们只是将状态设置为404
,这意味着not found
我们的标准故障响应或{ status, message }
Cool, right? ????
酷吧? ????
Next we simply do this:
接下来,我们只需执行以下操作:
// If todo is found
response.status = 200;
response.body = {
success: true,
data: todo,
};
Set a 200
success response and in our response body we set success: true & data: todo
.
设置200
成功响应,然后在响应正文中设置success: true & data: todo
。
Let's run this in our postman.
让我们在邮递员中运行它。
Let's restart our server:
让我们重新启动服务器:
$ deno run --allow-net server.ts
In my postman, I open up a new tab. Set the request to GET
type and in the URL
bar type http://localhost:8080/todos/:id
, then hit Send
.
在邮递员中,我打开一个新选项卡。 将请求设置为GET
类型,然后在URL
栏中键入http://localhost:8080/todos/:id
,然后点击Send
。
Since we are generating ID's randomly, first get all todos by hitting theget all todos API. Then from any todo get one of its ID to test this newly created API.Every time you restart this Deno application, new ID's will be generated.
由于我们是随机生成ID的,因此首先请点击获取所有待办事项API来获取所有待办事项。 然后从任何待办事项中获取其ID之一来测试此新创建的API。每次重新启动此Deno应用程序时,都会生成新的ID。
Let's go:
我们走吧:
If you need to reference the original source code of this tutorial go here.
如果您需要参考本教程的原始源代码,请转到此处 。
Great, 3 APIs done, 2 more to go.
太好了,已完成3个API,还有2个尚待解决。
import { v4 } from "https://deno.land/std/uuid/mod.ts";
// interfaces
import Todo from "../interfaces/Todo.ts";
// stubs
import todos from "../stubs/todos.ts";
export default {
getAllTodos: () => {},
createTodo: async () => {},
getTodoById: () => {},
/**
* @description Update todo by id
* @route PUT todos/:id
*/
updateTodoById: async (
{ params, request, response }: {
params: { id: string },
request: any,
response: any,
},
) => {
const todo: Todo | undefined = todos.find((t) => t.id === params.id);
if (!todo) {
response.status = 404;
response.body = {
success: false,
message: "No todo found",
};
return;
}
// if todo found then update todo
const body = await request.body();
const updatedData: { todo?: string; isCompleted?: boolean } = body.value;
let newTodos = todos.map((t) => {
return t.id === params.id ? { ...t, ...updatedData } : t;
});
response.status = 200;
response.body = {
success: true,
data: newTodos,
};
},
deleteTodoById: () => {},
};
Let's talk about our controller for PUT todos/:id
. This will update a todo by ID.
我们来谈谈用于PUT todos/:id
控制器。 这将通过ID更新待办事项。
Let's break this down into smaller bits:
让我们将其分解为更小的部分:
const todo: Todo | undefined = todos.find((t) => t.id === params.id);
if (!todo) {
response.status = 404;
response.body = {
success: false,
message: "No todo found",
};
return;
}
This is something we did exactly the same with the previous controller as well, so I won't go into much detail here.
这也是我们对以前的控制器所做的完全相同的事情,因此在这里我将不做太多详细介绍。
Pro tip here: You can if you want make this piece of code a generic code block and then use it in both controllers.
这里的专业提示:您可以将这部分代码设为通用代码块,然后在两个控制器中使用它。
Next we do this:
接下来,我们这样做:
// if todo found then update todo
const body = await request.body();
const updatedData: { todo?: string; isCompleted?: boolean } = body.value;
let newTodos = todos.map((t) => {
return t.id === params.id ? { ...t, ...updatedData } : t;
});
response.status = 200;
response.body = {
success: true,
data: newTodos,
};
The piece of code I want to talk about here is the following:
我想在这里讨论的代码如下:
const updatedData: { todo?: string; isCompleted?: boolean } = body.value;
let newTodos = todos.map((t) => {
return t.id === params.id ? { ...t, ...updatedData } : t;
});
First we do const updatedData = body.value
and then add type checking to updatedData
like the following:
首先,我们执行const updatedData = body.value
,然后将类型检查添加到updatedData
,如下所示:
updatedData: { todo?: string; isCompleted?: boolean }
This piece of code is telling TS that updatedData
is an object which can have/not have
todo: string and also can have/not have
isCompleted: boolean.
这段代码告诉TS, updatedData
是一个对象,可以have/not have
todo:字符串,也可以have/not have
isCompleted:布尔值。
Then we simply map over all todos like this:
然后,我们只需像这样映射所有待办事项:
let newTodos = todos.map((t) => {
return t.id === params.id ? { ...t, ...updatedData } : t;
});
And where params.id
match with t.id
we simply append everything to that object we get from the user.
在params.id
与t.id
匹配的t.id
我们只需将所有内容附加到从用户那里获得的对象。
We are done with this API as well.
我们也已经完成了此API。
Let's restart our server:
让我们重新启动服务器:
$ deno run --allow-net server.ts
Open up a new tab in Postman. Set the request to PUT
and in the URL
bar type in http://localhost:8080/todos/:id
, then hit Send
:
在邮递员中打开一个新标签。 将请求设置为PUT
并在URL
栏中输入http://localhost:8080/todos/:id
,然后点击Send
:
Since we are generating ID's randomly, first get all todos by hitting get all todos API. Then from any todo get one of its ID to test this newly created API.Every time you restart this Deno application, new ID's will be generated.
由于我们是随机生成ID,因此首先请点击获取所有待办事项API来获取所有待办事项。 然后从任何待办事项中获取其ID之一来测试此新创建的API。每次重新启动此Deno应用程序时,都会生成新的ID。
This is amazing – four APIs done and just one more to go.
太神奇了–完成了四个API,还剩下一个。
import { v4 } from "https://deno.land/std/uuid/mod.ts";
// interfaces
import Todo from "../interfaces/Todo.ts";
// stubs
import todos from "../stubs/todos.ts";
export default {
getAllTodos: () => {},
createTodo: async () => {},
getTodoById: () => {},
updateTodoById: async () => {},
/**
* @description Delete todo by id
* @route DELETE todos/:id
*/
deleteTodoById: (
{ params, response }: { params: { id: string }; response: any },
) => {
const allTodos = todos.filter((t) => t.id !== params.id);
// remove the todo w.r.t id and return
// remaining todos
response.status = 200;
response.body = {
success: true,
data: allTodos,
};
},
};
Let's talk about our controller for Delete todos/:id
this will delete a todo by ID.
让我们说说我们的Delete todos/:id
控制器,它将通过ID删除待办事项。
We simply run a filter on all todos:
我们只需对所有待办事项运行过滤器:
const allTodos = todos.filter((t) => t.id !== params.id);
Remove the todo.id
that matches with params.id
and return the rest.
删除与params.id
匹配的todo.id
并返回其余的。
Then we do this:
然后我们这样做:
// remove the todo w.r.t id and return
// remaining todos
response.status = 200;
response.body = {
success: true,
data: allTodos,
};
Simply return all the todos left which do not have the same todo.id.
只需返回所有没有相同todo.id的待办事项即可。
Let's restart our server:
让我们重新启动服务器:
$ deno run --allow-net server.ts
Open up a new tab in Postman. This time set the request to DELETE
and in the URL
bar type http://localhost:8080/todos/:id
and hit Send
.
在邮递员中打开一个新标签。 这次将请求设置为DELETE
然后在URL
栏中键入http://localhost:8080/todos/:id
并点击Send
。
Since we are generating ID's randomly, first get all todos by hitting get all todos API. Then from any todo get one of its ID to test this newly created API.Every time you restart this Deno application, new ID's will be generated.
由于我们是随机生成ID,因此首先请点击获取所有待办事项API来获取所有待办事项。 然后从任何待办事项中获取其ID之一来测试此新创建的API。每次重新启动此Deno应用程序时,都会生成新的ID。
With this we are all done with all five APIs.
这样,我们就完成了所有五个API。
Now we only have two things remaining:
现在我们只剩下两件事了:
- Add a not found route middleware so that when the user tries to access an unknown route it gives an error. 添加一个未找到的路由中间件,以便当用户尝试访问未知路由时出现错误。
- Add a logger API that consoles the response time it took to return data from one API endpoint. 添加一个记录器API,该记录器控制从一个API端点返回数据所花费的响应时间。
为找不到的路由创建路由中间件 (Creating a route middleware for routes that aren't found)
In your root folder create a new folder called middlewares
. Inside that folder create a file called notFound.ts
and inside this file add this code:
在您的根文件夹中,创建一个名为middlewares
的新文件夹。 在该文件夹中创建一个名为notFound.ts
的文件,并在该文件中添加以下代码:
export default ({ response }: { response: any }) => {
response.status = 404;
response.body = {
success: false,
message: "404 - Not found.",
};
};
Here we aren't doing anything new – it is very similar to our controllers structure. Just returning a status 404
(which means not found) along with a JSON object for { success, message }
.
在这里,我们没有做任何新的事情-它与我们的控制器结构非常相似。 只需返回状态404
(表示未找到)以及用于{ success, message }
的JSON对象。
Next go in your server.ts
file and add the following content:
接下来进入您的server.ts
文件并添加以下内容:
- Add this import somewhere at the top: 将此导入添加到顶部的某个位置:
// not found
import notFound from './middlewares/notFound.ts';
-
And then just below your
app.use(todoRouter.allowedMethods())
add this line like this:然后在您的
app.use(todoRouter.allowedMethods())
下方添加此行,如下所示:
app.use(todoRouter.routes());
app.use(todoRouter.allowedMethods());
// 404 page
app.use(notFound);
The order of execution is important here: every time we try to access an API end point it will first match/check routes from our todoRouter
. If none are found, it will then execute app.use(notFound);
.
执行顺序在这里很重要:每次我们尝试访问API端点时,它都会首先匹配/检查来自todoRouter
路由。 如果没有找到,它将执行app .use (notFound);
。
Let's see if this works.
让我们看看这是否有效。
Restart the server:
重新启动服务器:
$ deno run --allow-net server.ts
Open up a new tab in Postman. Set the request to GET
and in the URL
bar type http://localhost:8080/something-unknown
, then hit Send
.
在邮递员中打开一个新标签。 将请求设置为GET
然后在URL
栏中键入http://localhost:8080/something-unknown
,然后单击Send
。
So we now have a route middleware that we put at the end of our routes in server.ts
as app.use(notFound);
. If no route matches this middleware it will execute and return a 404
status code (which means not found). Then we simply send a response message like always which is {success, message}
.
因此,我们现在有了一个路由中间件,将它放置在server.ts
路由的末尾,作为app .use (notFound);
。 如果没有路由与该中间件匹配,它将执行并返回404
状态代码(表示未找到)。 然后,我们像往常一样简单地发送一个响应消息,即{success, message}
。
Pro tip: We have decided that {success, message}
is what we return in failed scenarios and {success, data}
is what we return to user in success scenarios. So we can even make these to object/shapes as interfaces and add them to our project to ensure consistency and safe type checking.
专家提示:我们已经确定, {success, message}
是失败情况下的返回结果, {success, data}
是我们在成功情况下返回的结果。 因此,我们甚至可以将它们作为对象/形状作为接口,并将它们添加到我们的项目中,以确保一致性和安全的类型检查。
Cool, now we are done with one of our middlewares – let's add the other middleware for logging our APIs in the console.
太酷了,现在我们完成了其中一个中间件的工作–让我们添加另一个中间件来在控制台中记录我们的API。
Reminder: If you get stuck anywhere you can use the source code here.
在控制台中记录API (Logging APIs in console)
In your middlewares
folder create a new file called logger.ts
and enter the following code:
在您的middlewares
文件夹中,创建一个名为logger.ts
的新文件,并输入以下代码:
import {
green,
cyan,
white,
bgRed,
} from "https://deno.land/aaa@qq.com/fmt/colors.ts";
const X_RESPONSE_TIME: string = "X-Response-Time";
export default {
logger: async (
{ response, request }: { response: any, request: any },
next: Function,
) => {
await next();
const responseTime = response.headers.get(X_RESPONSE_TIME);
console.log(`${green(request.method)} ${cyan(request.url.pathname)}`);
console.log(`${bgRed(white(String(responseTime)))}`);
},
responseTime: async (
{ response }: { response: any },
next: Function,
) => {
const start = Date.now();
await next();
const ms: number = Date.now() - start;
response.headers.set(X_RESPONSE_TIME, `${ms}ms`)
},
};
In your server.ts
file add this code:
在您的server.ts
文件中添加以下代码:
- Import this somewhere at the top: 将此导入到顶部的某个位置:
// logger
import logger from './middlewares/logger.ts';
-
Just above your
todoRouter
code add these middlewares like this:在您的
todoRouter
代码上方,添加这些中间件,如下所示:
// order of execution is important;
app.use(logger.logger);
app.use(logger.responseTime);
app.use(todoRouter.routes());
app.use(todoRouter.allowedMethods());
Now let's discuss what we just did.
现在让我们讨论一下我们刚刚做了什么。
Let's talk about the logger.ts
file and break it down into bits:
让我们谈谈logger.ts
文件并将其分解为几部分:
import {
green,
cyan,
white,
bgRed,
} from "https://deno.land/aaa@qq.com/fmt/colors.ts";
I am importing some console colors and console background colors that I want to use in API logging.
我正在导入一些我想在API日志记录中使用的控制台颜色和控制台背景颜色。
This is similar to what we did in our eventListener
in our server.ts
file. We will use colors in our console to log API requests.
这类似于我们在server.ts
文件中的eventListener
中执行的操作。 我们将在控制台中使用颜色来记录API请求。
Next I set const X_RESPONSE_TIME: string = "X-Response-Time";
. This is the header we will inject in our API requests as they come into our server. I am calling this
and its value is X_RESPONSE_TIME
. I will demonstrate its usage in a bit.X-Response-Time
接下来,我设置const X_RESPONSE_TIME: string = "X-Response-Time";
。 这是我们将在API请求进入服务器时注入的标头。 我将其称为
,其值为X_RESPONSE_TIME
。 我将稍作演示。 X-Response-Time
Next we simply export an object like this:
接下来,我们简单地导出这样的对象:
export default {
logger: async ({ response, request }, next) {}
responseTime: async ({ response }, next) {}
};
And then we simply use it inside our server.ts
file like this:
然后,我们只需在server.ts
文件中使用它,如下所示:
// order of execution is important;
app.use(logger.logger);
app.use(logger.responseTime);
Let's now discuss what is happening in our logger middleware code and discuss it execution style using next()
:
现在让我们讨论记录器中间件代码中正在发生的事情,并使用next()
讨论它的执行方式:
The only difference here and in the controllers we had before is the use of the next()
function. This functions helps us jump from one controller to the other as shown in the image below.
这里和以前的控制器之间唯一的区别是使用next()
函数。 此功能有助于我们从一个控制器跳到另一个控制器,如下图所示。
So in:
所以在:
export default {
logger: async (
{ response, request }: { response: any, request: any },
next: Function,
) => {
await next();
const responseTime = response.headers.get(X_RESPONSE_TIME);
console.log(`${green(request.method)} ${cyan(request.url.pathname)}`);
console.log(`${bgRed(white(String(responseTime)))}`);
},
responseTime: async (
{ response }: { response: any },
next: Function,
) => {
const start = Date.now();
await next();
const ms: number = Date.now() - start;
response.headers.set(X_RESPONSE_TIME, `${ms}ms`)
},
};
Keep in mind that this is what we have in our server.ts
file:
请记住,这就是server.ts
文件中的内容:
// order of execution is important;
app.use(logger.logger);
app.use(logger.responseTime);
app.use(todoRouter.routes());
app.use(todoRouter.allowedMethods());
The order of execution is as follows:
执行顺序如下:
- logger.logger middleware logger.logger中间件
- logger.responseTime middleware logger.responseTime中间件
-
todoRouter controller (whatever path is called by the user, for the purpose of explanation I am assuming that the user called
GET /todos
API to get all todos.)todoRouter控制器(无论用户调用什么路径,出于解释的目的,我假设用户都调用
GET /todos
API来获取所有待办事项。)
So it will first execute logger.logger middleware which is this:
因此,它将首先执行logger.logger中间件,它是:
logger: async (
{ response, request }: { response: any, request: any },
next: Function,
) => {
await next();
const responseTime = response.headers.get(X_RESPONSE_TIME);
console.log(`${green(request.method)} ${cyan(request.url.pathname)}`);
console.log(`${bgRed(white(String(responseTime)))}`);
},
It will come inside this function and immediately as it reads await next()
it quickly jumps to the next middleware which is responseTime
:
它将进入此函数,并在读取await next()
时立即跳转到下一个中间件responseTime
:
Inside responseTime
, it only executes two lines which are (look at execution order 2 in image above):
在responseTime
内部,它仅执行以下两行(请参见上图中的执行顺序2):
const start = Date.now();
await next();
before jumping to the getAllTodos
controller. Once it goes inside getAllTodos
it will run the entire code inside that controller.
然后跳转到getAllTodos
控制器。 一旦进入getAllTodos
,它将在该控制器中运行整个代码。
Since in that controller we are not using next()
it will simply return the flow of logic back to responseTime
controller. There it will run the following:
由于在该控制器中我们没有使用next()
因此它将简单地将逻辑流返回给responseTime
控制器。 在那里它将运行以下命令:
const ms: number = Date.now() - start;
response.headers.set(X_RESPONSE_TIME, `${ms}ms`)
Now keeping in perspective of the order of execution which is 2, 3, 4
(look at the image above).
现在,我们将保持执行顺序为2, 3, 4
(请参见上图)。
This is what happens:
这是发生了什么:
-
We capture the data in
ms
by doingconst
start
=
Date.now
();
. Then we immediately callnext()
which goes togetAllTodos
controller and runs the entire code. Then it comes back in theresponseTime
controller.我们通过执行
const
start
=
Date.now
();
以ms
为单位捕获数据();
。 然后,我们立即调用next()
,它将转到getAllTodos
控制器并运行整个代码。 然后,它返回到responseTime
控制器中。 -
We then subtract that
start
date with whatever the date is at that moment by doingconst ms: number = Date.now() - start;
ms
. Here it will return a number which is basically the difference in milliseconds that will tell us all the time it took Deno to execute ourgetAllTodos
controller.然后,我们减去
start
与任何的日期是在那一刻做日期const ms : number = Date.now () - start ;
const ms : number = Date.now () - start ;
ms
。 在这里它将返回一个数字,该数字基本上是毫秒差,它将告诉我们getAllTodos
执行我们的getAllTodos
控制器所花费的所有时间。
Sharing the image once again for review:
再次共享图像以供查看:
-
Next we simply set headers in our
response
like this:接下来,我们只需在
response
设置标头,如下所示:
response.headers.set(X_RESPONSE_TIME, `${ms}ms`)
Which just sets the header value X-Response-Time
to the milliseconds it took Deno to execute our API.
只是将标头值X-Response-Time
设置为Deno执行我们的API所花费的毫秒数。
-
Then from execution order
4
we move back to execution order5
(have a look at the image above for reference).然后从执行顺序
4
返回到执行顺序5
(请看上图以供参考)。
Here we simply do:
在这里,我们只是做:
const responseTime = response.headers.get(X_RESPONSE_TIME);
console.log(`${green(request.method)} ${cyan(request.url.pathname)}`);
console.log(`${bgRed(white(String(responseTime)))}`);
-
We get the time we passed in the
X-Response-Time
我们获得了
经过X-Response-Time
X-Response-Time
- Then we take that time and simply console it colourfully in the console. 然后,我们花时间在控制台中简单地对其进行丰富多彩的控制。
request.method
tells us the method used to call our API, that is GET, PUT etc
while request.url.pathname
will tell the API which path the user used i.e, /todos
request.method
告诉我们用于调用API的方法,即GET, PUT etc
而request.url.pathname
告诉我们用户使用的路径,即/todos
Let's see if this works.
让我们看看这是否有效。
Restart the server:
重新启动服务器:
$ deno run --allow-net server.ts
Open up a new tab in Postman. Set the request to GET
, type in http://localhost:8080/todos
, and hit Send
.
在邮递员中打开一个新标签。 将请求设置为GET
,输入http://localhost:8080/todos
,然后单击Send
。
Hit the API a couple of times in Postman. Then when you go back to the console, you should see something like this:
在Postman中几次点击API。 然后,当您回到控制台时,应该看到类似以下的内容:
This is it – we are done.
就这样–我们完成了。
If you still feel stuck, take a look at the entire source code for this tutorial here: github.com/adeelibr/deno-playground/tree/master/chapter_1:oak
如果您仍然感到困惑,请在此处查看本教程的整个源代码: github.com/adeelibr/deno-playground/tree/master/chapter_1 : oak
I hope that you found this article useful and that it was able to help you learn something today.
我希望您觉得这篇文章有用,并且可以帮助您今天学到一些东西。
If you liked it, please do share it on social media. If you want to have a discussion about it, reach out to me on Twitter.
如果喜欢,请在社交媒体上分享。 如果您想对此进行讨论,请通过Twitter与我联系。
翻译自: https://www.freecodecamp.org/news/create-a-todo-api-in-deno-written-by-a-guy-coming-from-node/
node deno
下一篇: IIS文件解析漏洞