引言
在本文中,我想与大家分享我从头开始使用TypeScript在Node.js上开发第一个REST API的情感和所学技能。 这个故事很平庸:
“我毕业于大学,获得了文凭。 在哪里上班?” 您可能已经猜到我了,即使我不必考虑太多,问题也无法幸免。 开发人员(同一专业的毕业生)要求实习。 我相信这是相当普遍的做法,并且有许多类似的故事。 我三思而后行,决定试着去...

第一天 介绍Node.js
我来到了后端开发。 该IT公司使用我完全不熟悉的
Node.js平台。 我向前跑了一下,忘记告诉读者我从未用JavaScript开发过任何东西(除了几个带有复制代码的脚本之外)。 自从我用Java,Python和Clojure开发CRUD以来,我就整体上了解了工作算法和Web应用程序的体系结构,但这还不够。 因此,在我完全致力于研究Node.js的第一天,这个
截屏视频确实有所帮助。
在研究
Express Web框架,
npm软件包管理器以及package.json和tsconfig.json之类的文件时,我的头脑只是绕过了很多信息。 另一个教训是,同时掌握所有材料几乎是不可能完成的任务。 到最后,我仍然设法配置了环境,并且能够运行Express Web服务器! 但是现在高兴还为时过早,因为他带着一种完全的误解回到家。 我被淹没在JS的广阔世界中的感觉并没有让我离开一分钟,因此需要重新启动。
第二天 介绍TypeScript
当天又进行了相同的重启。 至此,我完全意识到了我的问题,我们将继续进行讨论。 知道不必用纯JavaScipt编写,从Node.js进行的培训就顺利地流向TypeScript语言,即其功能和语法。 在这里,我看到了期待已久的
类型 ,没有
类型的编程实际上是两天前的,
而不是使用函数式编程语言。 这是我最大的误解,这使我在第一天就无法理解和学习用JavaScript编写的代码。
他以前大部分时间是使用面向对象的编程语言(例如Java,C ++,C#)编写的。 意识到TypeScript的可能性,我感到很轻松。 这种编程语言从字面上使我感受到了当时那个复杂环境的生命。 在一天结束之前,我完全设置了环境,启动了服务器(已经在TypeScript上使用),连接了必要的库,我将在下面进行讨论。 底线:准备开发API。 我们直接通过发展...
API开发
我们将离开对工作原理的解释以及对REST API的其他解释,因为该论坛上有很多与此相关的文章,包括示例和各种编程语言的开发。
任务如下:使用REST API进行服务。 通过承载令牌(/信息,/延迟,/注销)进行授权。 配置为可从任何域访问的CORS。 DB-MongoDB。 在每次通话时创建一个令牌。
API说明:- /登录[POST]-通过ID和密码请求令牌承载// //接收JSON中的数据
- / signup [POST]-注册新用户://接收json中的数据
- / info [GET]-返回用户ID和ID类型,要求在身份验证中由承载者发行的令牌
- /延迟[GET]-返回延迟(ping),要求承载者在身份验证中发出的令牌
- /注销[GET]-使用参数all:true-删除所有用户承载令牌,或者false-仅删除当前承载令牌
我立即注意到,对于Web应用程序开发人员而言,该任务看起来非常简单。 但是该任务必须用一种编程语言来实现,大约三天前它根本什么都不知道! 即使是对我来说,它在纸上看起来也是完全透明的,在Python中,该实现花费了一些时间,但我没有这种选择。 开发堆栈预示着麻烦。
实施手段
因此,我提到第二天我已经研究了几个库(框架),我们将从此开始。 对于路由,我选择了
路由控制器 ,并与Spring框架(Java)的装饰器进行了许多相似的选择。 作为ORM,我选择了
typeorm ,尽管以实验模式与MongoDB一起工作,但足以完成这样的任务。 我使用
uuid生成令牌,使用
dotenv加载变量。
Web服务器启动
通常,express是以其纯形式使用的,但是我提到了路由控制器框架,该框架使我们可以如下创建一个express服务器:
如您所见,没有什么复杂的。 实际上,该框架具有更多功能,但不需要它们。
- routePrefix只是服务器地址后面URL中的前缀,例如: localhost :3000 / prefix
- 默认值-没什么有趣的,只需初始化错误代码
- authorizationChecker-框架检查用户授权的绝佳机会,然后我们将更详细地考虑
- 控制器是我们指定应用程序中使用的控制器的主要字段之一
数据库连接
之前,我们已经启动了Web服务器,因此我们将继续连接到MongoDB数据库,之前已将其部署在本地服务器上。
官方文档中详细描述了安装和配置。 我们将直接使用typeorm考虑连接:
一切都非常简单,您需要指定几个参数:
- 类型-DB
- 主机-部署数据库的IP地址
- database-先前在mongodb中创建的数据库的名称
- 同步-与数据库自动同步(注意:当时很难掌握迁移情况)
- 实体-在这里,我们指示执行同步的实体
现在,我们将服务器启动并连接到数据库。 我注意到资源的导入与Node.js中使用的经典资源不同。 结果,我们得到以下可执行文件,在我的情况下为main.ts:
import 'reflect-metadata'; import * as dotenv from 'dotenv'; import { createExpressServer } from 'routing-controllers'; import { createConnection } from 'typeorm'; import { authorizationChecker } from './auth/authorizationChecker'; import { UserController } from './controllers/UserController'; import { User } from './models/User'; dotenv.config();
实体
让我提醒您,任务是分别对用户进行身份验证和授权,我们需要一个实体:User。 但这还不是全部,因为每个用户都有一个令牌而不是一个令牌! 因此,有必要创建一个令牌实体。
用户名 import { ObjectID } from 'bson'; import { IsEmail, MinLength } from 'class-validator'; import { Column, Entity, ObjectIdColumn } from 'typeorm'; import { Token } from './Token';
在“用户”表中,我们创建一个字段-用户的非常标记的数组。 我们还
启用了calss-validator ,因为用户必须通过电子邮件登录。
代币 import { Column, Entity } from 'typeorm';
基数如下:

用户授权
对于授权,我们使用
authorizationChecker (创建服务器时的参数之一,请参见上文),为方便起见,我们将其放在单独的文件中:
import { Action, UnauthorizedError } from 'routing-controllers'; import { getMongoRepository } from 'typeorm'; import { User } from '../models/User'; export async function authorizationChecker(action: Action): Promise<boolean> { let token: string; if (action.request.headers.authorization) {
经过身份验证后,每个用户都有自己的令牌,因此我们可以从响应的标头中获取必要的令牌,如下所示:
Bearer 046a5f60-c55e-11e9-af71-c75526de439e 。 现在我们可以检查此令牌是否存在,此后函数将返回授权信息:true-用户已授权,false-用户未授权。 在应用程序中,我们可以在控制器中使用非常方便的装饰器:@Authorized()。 此时,将调用authorizationChecker函数,该函数将返回响应。
逻辑学
首先,我要描述业务逻辑,因为控制器是所提供类之下的一行方法调用。 另外,在控制器中,我们将接受所有数据,在本例中为JSON和Query。 我们将考虑用于各个任务的方法,最后我们将形成最终文件,称为UserService.ts。 我注意到,那时根本没有足够的知识来消除依赖关系。 如果您还没有满足依赖注入一词,我强烈建议您阅读一下。 目前,我使用DI框架,即使用容器,即通过构造函数注入。 我认为这是一篇很好的评论
文章 。 我们回到任务。
- /登录[POST] -注册用户的身份验证。 一切都非常简单和透明。 我们只需要在数据库中找到该用户并发出新令牌即可。 对于读写,使用MongoRepository。
async userSignin(user: User): Promise<string> {
- /注册[POST] -注册新用户。 一种非常相似的方法,因为起初我们也在寻找用户,因此我们没有使用一封电子邮件注册用户。 接下来,在发出令牌之后,我们将新用户写入数据库。
async userSignup(newUser: User): Promise<string> {
- / info [GET] -返回用户ID和ID类型,要求在身份验证中由承载者发行的令牌。 图片也是透明的:首先我们从请求标头中获得用户的当前令牌,然后在数据库中查找它并确定它位于谁,然后返回找到的用户。
async getUserInfo(req: express.Request): Promise<User> {
- /延迟[GET] -返回延迟(ping),要求承载中的令牌在身份验证中发出。 不过,这篇文章完全没有意思。 在这里,我仅使用一个现成的库来检查tcp-ping延迟。
getLatency(): Promise<IPingResult> { function update(progress: number, total: number): void { console.log(progress, '/', total); } const latency = ping({ address: process.env.PING_ADRESS, attempts: Number(process.env.PING_ATTEMPTS), port: Number(process.env.PING_PORT), timeout: Number(process.env.PING_TIMEOUT) }, update).then(result => { console.log('ping result:', result); return result; }); return latency; }
- /注销[GET] -使用参数all:true-删除所有用户承载令牌,或者false-仅删除当前承载令牌。 我们只需要找到用户,检查查询参数并删除令牌即可。 我认为一切都应该清楚。
async userLogout(all: boolean, req: express.Request): Promise<void> {
控制者
许多人不需要解释什么以及在MVC模式中如何使用控制器,但是我仍然会讲两个词。 简而言之,控制器是用户和应用程序之间的链接,用于在用户和应用程序之间重定向数据。 上面已经对逻辑进行了完整的描述,其逻辑根据路由来调用,路由由URI和ip服务器组成
(例如:localhost:3000 / signin) 。 我之前提到控制器中的装饰器:
Get ,
POST ,@Authorized,其中最重要的是@JsonController。 该框架的另一个非常重要的功能是,如果我们要发送和接收JSON,则可以使用此装饰器而不是
Controller 。
import * as express from 'express'; import { Authorized, Body, Get, Header, JsonController, NotFoundError, Post, QueryParam, Req, UnauthorizedError } from 'routing-controllers'; import { IPingResult } from '@network-utils/tcp-ping'; import { User } from '../models/User'; import { UserService } from '../services/UserService';
结论
在本文中,我不想再反映正确代码或类似内容的技术组成部分,而只是分享一个事实,即一个人可以使用数据库构建一个Web应用程序,并在
五天内至少包含一些绝对零值的逻辑。 只需考虑一下,没有一种乐器是您熟悉的,记住自己或将它放在我的位置。 这种情况绝不会说:“我是最好的,你永远都做不到。” 相反,这是一个人的灵魂的呐喊,该人目前对Node.js的世界完全满意并与您分享。 事实上,没有什么是不可能的,您只需要采取行动即可!
当然,不能否认作者一无所知,坐下来第一次编写代码。 不,OOP的知识,REST API的原理,ORM和数据库的数量足够多。 这只能说,取得成果的方法绝对不会发挥任何作用,并以这种方式说:“我不会去做这份工作,有一种我没有学过的编程语言”,对我来说,这只是一个人的表现,不是弱点,而是保护免受陌生的外部环境。 但是隐藏的地方是,恐惧与我同在。
总结一下。 我想建议尚未开始从事IT事业的学生和人们,不要害怕开发工具和未知技术。 高级同志一定会帮助您(如果您和我一样幸运的话),他们将详细解释并回答问题,因为他们每个人都处于这个位置。 但不要忘记,您的愿望是最重要的方面!
链接到
项目