目录
介绍
背景
使用代码
项目设置
兴趣点
参考
- 下载源代码 - 60.8 KB
Nodejs使您能够使用JavaScript编写服务器端代码。实际上,使用nodejs创建Web服务器非常容易和快速,并且构建了许多框架,这使得开发更加容易和快速。
但Nodejs的发展面临的挑战并不多:
- Node js都是关于回调的,并且随着越来越多的回调,你最终会遇到一个叫做回调地狱的情况
- 编写可读代码
- 编写可维护的代码
- 你没有得到太多智能感知支持,这使得开发变得缓慢
如果您有经验并且对nodejs有很好的了解,那么您可以使用不同的技术并尽量减少这些挑战。
解决这些问题的最佳方法是使用现代JavaScript ES6,ES7或typescript,无论您感觉如何。我推荐使用typescript,因为它为每个代码字提供了intillisense支持,使您的开发更快。
所以我找到了一个框架fortjs——它非常易于使用,其概念非常酷。它的概念围绕创造一个堡垒。Fortjs使您能够编写模块化、安全且非常漂亮且可读的服务器端代码。
背景FortJs是一个MVC框架,其工作方式类似于真正的堡垒。它以组件的形式提供模块化。有三个组成部分:
- Wall——这是一个顶级组件,每个HTTP请求首先通过wall。
- Shield——这适用于控制器。您可以为控制器分配一个shield,它将在调用控制器之前执行。
- Guard——这适用于fort.js中名为worker的控制器内部的方法。在调用worker之前,将调用guard。
每个组件都像一个阻塞程序,如果条件不满足——它们会阻止请求进一步传递。就像在一个真正的堡垒——如果条件不满意,警卫会阻止任何人。
我希望您理解这一点并获取更多信息,请阅读组件文档——http://fortjs.info/tutorial/components/。
使用代码在本文中 - 我将使用fortjs和typescript语言创建REST API。但您也可以使用相同的代码和步骤用JavaScript来实现。
项目设置FortJs提供了一个CLI——fort-creator。这有助于您设置项目并加快开发速度。我也将使用CLI。
因此,按顺序执行以下步骤:
- 打开终端或命令提示符。
- 全局安装FortJs——运行命令“npm i fort-creator -g”。
- 创建一个新项目——运行命令“fort-creator new my-app”。这里“my-app”是应用程序的名称,您可以选择任何其他名字。这将提示您选择具有两个选项的语言:typescript和javascript。使用箭头键选择语言,然后按Enter键。因为我要使用typescript,所以我选择了typescript。创建项目需要一些时间。
- 进入项目目录——“cd my-app”。
- 使用热重新加载启动开发服务器——运行命令“ fort-creator start”。
- 打开浏览器并输入URL——http://localhost:4000/.。
您应该在浏览器中看到类似的内容。
让我们看看如何呈现此页面:
- 在您喜欢的代码编辑器中打开项目文件夹,我将使用vscode。您将在项目根目录中看到许多文件夹,例如控制器,视图等。每个文件夹按其用途分组,例如controllers文件夹包含所有控制器和视图文件夹包含所有视图。
- 打开控制器文件夹 ->在控制器内部,您将看到一个文件名——default_controller,让我们打开它并观察代码。该文件包含一个类DefaultController——这是一个控制器类,它包含返回一些http响应的方法。
- 在DefaultController类中->您将看到一个方法'index'——这是将输出呈现给浏览器的方法。该方法在fortjs中称为worker,因为它们执行某种工作并将结果作为http响应返回。让我们观察index方法代码:
try {
const data= {
title: 'FortJs'
};
const result = await viewResult('default/index.html', data);
return result;
} catch (ex) {
// handle exception and show user a good message.
// save this ex in a file or db, so that you can know what's the issue and where
const result = await textResult("Our server is busy right now. Please try later.");
return result;
}
它创建一个数据对象并将该对象传递给viewResult方法。该viewResult方法获取视图位置和视图数据。此方法的工作是呈现我们在浏览器中看到的视图和返回响应。
- 我们来观察一下视图代码。打开视图文件夹 ->打开默认文件夹 ->打开index.html。这是我们的视图代码。它是简单的HTML代码以及一些mustache 语法。Fortjs默认视图引擎是mustache 。
REST
我们将为实体用户创建一个REST端点——它将为用户执行CRUD操作,例如添加用户、删除用户、获取用户和更新用户。
根据REST:
- 添加用户——应该使用http方法“POST”
- 删除用户——应该使用http方法“REMOVE”
- 获取用户——应该使用http方法完成“GET”
- 更新用户——应该使用http方法“PUT”
为了创建端点,我们需要创建一个类似于前面解释的默认控制器的Controller。
执行命令——“fort-creator add”。它会询问“what do you want to add ?”选择控制器并按回车键。输入控制器名称“UserController”并按Enter键。
现在我们已经创建了用户控制器,我们需要将它添加到路由中。由于我们的实体是用户,因此“/user”将是一条很好的路由。让我们添加它——在项目的根目录中打开routes.ts并添加UserController到路由。
所以我们的routes.ts看起来像这样:
import { DefaultController } from "./controllers/default_controller";
import { ParentRoute } from "fortjs";
import { UserController } from "./controllers/user_controller";
export const routes: ParentRoute[] = [{
path: "/default",
controller: DefaultController
}, {
path: "/user",
controller: UserController
}];
现在fortjs知道当路由——“/user ”被调用时,它需要调用UserController。让我们打开网址——http://localhost:4000/user。
你看到一个白页了吗?
这是因为——我们没有从index方法返回任何东西。让我们从index方法中返回一个文本“Hello World”。在index方法内添加以下代码并保存:
return textResult('Hello World');
刷新URL—— http://localhost:4000/user
而且你将看Hello World。
现在,让我们转换UserController为REST API。但在编写REST API代码之前,让我们创建一个虚拟服务,为用户进行crud操作。
MODEL
让我们创建一个代表实体用户的模型“User”。创建一个文件夹models和在文件夹内的文件user.ts。将以下代码粘贴到文件中:
export class User {
id?: number;
name: string;
gender: string;
address: string;
emailId: string;
password: string;
constructor(user) {
this.id = Number(user.id);
this.name = user.name;
this.gender = user.gender;
this.address = user.address;
this.emailId = user.emailId;
this.password = user.password;
}
}
SERVICE
创建文件夹“services ”,然后在文件夹中创建文件“user_service.ts ”。将以下代码粘贴到文件中。
import {
User
} from "../models/user";
interface IStore {
users: User[]
};
const store: IStore = {
users: [{
id: 1,
name: "durgesh",
address: "Bengaluru india",
emailId: "durgesh@imail.com",
gender: "male",
password: "admin"
}]
}
export class UserService {
getUsers() {
return store.users;
}
addUser(user: User) {
const lastUser = store.users[store.users.length - 1];
user.id = lastUser == null ? 1 : lastUser.id + 1;
store.users.push(user);
return user;
}
updateUser(user) {
const existingUser = store.users.find(qry => qry.id === user.id);
if (existingUser != null) {
existingUser.name = user.name;
existingUser.address = user.address;
existingUser.gender = user.gender;
existingUser.emailId = user.emailId;
return true;
}
return false;
}
getUser(id) {
return store.users.find(user => user.id === id);
}
removeUser(id) {
const index = store.users.findIndex(user => user.id === id);
store.users.splice(index, 1);
}
}
上面的代码包含一个变量存储,它包含一组用户,服务中的方法执行添加、更新、删除和获取该存储等操作。
我们将在REST API实现中使用此服务。
REST
GET
对于使用http方法“ GET”的路由“/user ”,API应返回所有用户的列表。为了实现这一点,让我们将“index”方法重命名为“getUsers”以使语义正确并将以下代码粘贴到方法中:
const service = new UserService();
return jsonResult(service.getUsers());
所以现在,user_controller.ts看起来像这样:
import { Controller, DefaultWorker, Worker, textResult, jsonResult } from "fortjs";
import { UserService } from "../services/user_service";
export class UserController extends Controller {
@DefaultWorker()
async getUsers() {
const service = new UserService();
return jsonResult(service.getUsers());
}
}
在这里,我们正在使用装饰器DefaultWorker。DefaultWorker做两件事情-它增加了路由“/”与HTTP方法“GET”。它是这种情况的捷径。在下一部分中,我们将使用其他装饰器来自定义路径。
让我们通过调用url http://localhost:4000/user来测试它。您可以在浏览器中打开它或使用任何http客户端工具,如postman或高级REST客户端。我正在使用高级REST客户端。
POST
让我们添加一个方法“addUser”,它将从请求体中提取数据并添加用户。
async addUser() {
const user: User = {
name: this.body.name,
gender: this.body.gender,
address: this.body.address,
emailId: this.body.emailId,
password: this.body.password
};
const service = new UserService();
const newUser = service.addUser(user);
return jsonResult(newUser, HTTP_STATUS_CODE.Created);
}
为了使这个方法对http请求可见,我们需要将其标记为worker。通过添加装饰器“Worker” 将方法标记为worker 。Worker装饰采用HTTP方法列表,并使该方法仅对那些HTTP方法可用。那么让我们添加装饰器:
@Worker([HTTP_METHOD.Post])
async addUser() {
const user: User = {
name: this.body.name,
gender: this.body.gender,
address: this.body.address,
emailId: this.body.emailId,
password: this.body.password
};
const service = new UserService();
const newUser = service.addUser(user);
return jsonResult(newUser, HTTP_STATUS_CODE.Created);
}
此方法的路由与“addUser” 方法的名称相同。您可以通过将post请求发送到 http://localhost:4000/user/addUser并在body中包含用户数据来检查这一点。
但我们希望路由为“/”,因此它是一个RESTAPI。使用装饰器“Route” 配置worker的路径。我们现在改变路由。
@Worker([HTTP_METHOD.Post])
@Route("/")
async addUser() {
const user: User = {
name: this.body.name,
gender: this.body.gender,
address: this.body.address,
emailId: this.body.emailId,
password: this.body.password
};
const service = new UserService();
const newUser = service.addUser(user);
return jsonResult(newUser, HTTP_STATUS_CODE.Created);
}
现在我们的终点配置为post请求。让我们通过将post请求发送到 http://localhost:4000/user/addUser并在body中包含用户数据来检查这一点。
我们已经为post请求创建了终点,但重要的是要验证数据。验证是服务器端Web应用程序中非常重要的一部分。
FortJs为这类工作提供组件Guard。
引用:
Guard是Worker上的安全层。它控制是否允许请求调用Worker。
所以我们将使用guard来验证数据。让我们用fort-creator来创造一个guard。执行命令——“fort-creator add”并选择Guard。输入文件名“UserValidatorGuard ”。将在guards文件夹中创建一个“user_validator_guard.ts ” 文件,打开该文件。
guard可以访问正文,因此您可以验证其中的数据。返回null内部方法检查意味着允许调用worker & 返回任何其他内容意味着阻止调用。
让我们通过编写验证代码来更深入地理解这一点。将以下代码粘贴到文件“user_validator_guard.ts”中:
import { Guard, HTTP_STATUS_CODE, textResult } from "fortjs";
import { User } from "../models/user";
export class UserValidatorGuard extends Guard {
isValidEmail(email: string) {
var re = /^(([^()\[\]\\.,;:\s@"]+(\.[^()\[\]\\.,;:\s@"]+)*)|
(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|
(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());
}
validate(user: User) {
let errMessage;
if (user.name == null || user.name.length < 5) {
errMessage = "name should be minimum 5 characters"
} else if (user.password == null || user.password.length < 5) {
errMessage = "password should be minimum 5 characters";
} else if (user.gender == null || ["male", "female"].indexOf(user.gender) < 0) {
errMessage = "gender should be either male or female";
} else if (user.emailId == null || !this.isValidEmail(user.emailId)) {
errMessage = "email not valid";
} else if (user.address == null || user.address.length < 10) {
errMessage = "address length should be greater than 10";
}
return errMessage;
}
async check() {
const user = new User(this.body);
const errMsg = this.validate(user);
if (errMsg == null) {
// pass user to worker method, so that they dont need to parse again
this.data.user = user;
// returning null means - this guard allows request to pass
return null;
} else {
return textResult(errMsg, HTTP_STATUS_CODE.BadRequest);
}
}
}
在上面的代码中:
- 我们创建了一个带参数用户的方法validate。如果存在验证错误,它将验证用户并返回错误消息,否则返回null。
- 我们在check方法中编写代码,这是守护生命周期的一部分。我们通过调用validate方法验证其中的用户。
- 如果用户有效,那么我们使用“data”属性传递用户值并返回null。返回null意味着guard已允许此请求,应该调用worker。
- 如果用户无效,我们将返回错误消息作为带有HTTP代码的文本响应——“badrequest”。在这种情况下,执行将在此处停止并且不会调用worker。
为了激活这个guard,我们需要为addUser方法添加它。通过使用装饰器“Guards”添加guard。所以让我们加上guard:
@Guards([UserValidatorGuard])
@Worker([HTTP_METHOD.Post])
@Route("/")
async addUser() {
const user: User = this.data.user;
const service = new UserService();
const newUser = service.addUser(user);
return jsonResult(newUser, HTTP_STATUS_CODE.Created);
}
在上面的代码中:
- 我已经使用Guards装饰器添加了guard ,“UserValidatorGuard”。
- 有了这个过程中的guard,我们不再需要在worker内部解析来自body的数据,而是从“ModelUserGuard”传递的this.data中读取数据。
- addUser只有当所有数据都有效时,才会调用方法“Guard” 。
需要注意的一点是——使用组件后方法“addUser”看起来很轻量 & 它也在进行验证。您可以向工作人员添加多个guard,使您能够将代码模块化为多个guard。
让我们尝试添加一些无效数据的用户:
正如您在图片中看到的,我尝试发送无效的电子邮件,结果是——“email not valid”。所以它意味着guard被激活并且完美地工作。
PUT
让我们添加另一种方法——用路由“/”的“updateUser”, guard——“ UserValidatorGuard”(用于验证用户)和最重要的——用http方法“PUT”的worker。
@Worker([HTTP_METHOD.Put])
@Guards([UserValidatorGuard])
@Route("/")
async updateUser() {
const user: User = this.data.user;
const service = new UserService();
const userUpdated = service.updateUser(user);
if (userUpdated === true) {
return textResult("user updated");
}
else {
return textResult("invalid user");
}
}
更新代码类似于插入代码,除了正在更新数据的功能。在这里,我们重新使用UserValidatorGuard以验证数据。
DELETE
为了删除数据,用户需要传递用户的id。这可以通过三种方式传递:
- 正如我们为添加和更新所做的那样在正文中发送数据
- 在查询字符串中发送数据
- 在路由中发送数据——为此,我们需要自定义路由
我们已经实现了从body获取数据。那么让我们看看另外两种方式:
在查询字符串中发送数据
让我们创建方法“removeByQueryString”并粘贴以下代码:
@Worker([HTTP_METHOD.Delete])
@Route("/")
async removeByQueryString() {
// taking id from query string
const userId = Number(this.query.id);
const service = new UserService();
const user = service.getUser(userId);
if (user != null) {
service.removeUser(userId);
return textResult("user deleted");
}
else {
return textResult("invalid user");
}
}
上面的代码非常简单。它从query控制器的属性获取id 并删除用户。我们来测试一下:
在路由中发送数据
您可以在路径中使用“{var}”来参数化路由。我们来看看怎么样?
让我们创建另一个方法“removeByRoute”并粘贴下面的代码:
@Worker([HTTP_METHOD.Delete])
@Route("/{id}")
async removeByRoute() {
// taking id from route
const userId = Number(this.param.id);
const service = new UserService();
const user = service.getUser(userId);
if (user != null) {
service.removeUser(userId);
return textResult("user deleted");
}
else {
return textResult("invalid user");
}
}
上面的代码和removeByQueryString完全相同,只是它从路由中提取id并使用路由中的参数,即“/{id}”,其中id是参数。
我们来测试一下:
因此,我们使用fortjs成功创建了REST API。
兴趣点为fortjs编写的代码清晰、可读和可维护。这些组件可帮助您模块化代码。
参考- http://fortjs.info/
- https://medium.com/fortjs/rest-api-using-typescript-94004d9ae5e6
原文地址:https://www.codeproject.com/Articles/5098856/NodeJs-Development-with-framework-fortjs