目录
- Table of contents
- 1. Prelude
- Disclaimer
- 2. Enough intro - show me the code.
- 2.1. Configuring the server
- 2.2. Example
- 2.3. Controllers and routes
- 2.4. Error handling
- 3. Here's the repo
- 4. There is a lot more to do
- 5. Outro
1.前奏
在我的上一份工作中,我正在处理一个由旧版后端 +前端代码库组成的项目,我在不断地重构代码 - 它是无法使用,完全不可读或两者兼而有之。为了使情况变得更糟,后端缺乏任何结构或目标 - 只是在大多数时候都设法工作的代码。在所有重构中,我发现自己慢慢地在代码库中添加了一些架构元素,因为这是稍后可以维护任何新代码的唯一方法。代码库甚至没有将控制器逻辑与服务逻辑分开!
随着时间的流逝,事情开始成形,我所有的建筑添加都使代码库的可用性有助于使用。由于当时离开Express并不是一个选择,但是许多新功能不断要求一些新的抽象和设计模式只是为了保持事情的井井有条,因此我慢慢地发现自己是为了我为自己的思考而定制的后端项目。
与此同时,我离开了这份工作,但是所有这些概念都粘在我身上,因此,作为一个学习项目,我决定将它们包裹在可以清洁,简单地使用的独立包装中。我以前从未写过一揽子计划,但我认为这将是一项简短,有趣且富有成效的努力。在这里,我正在与世界分享它,以便我们可以讨论,学习和弄清楚是什么使一个好的框架在一起。这样,我向您介绍: kamiq !
免责声明
在潜水之前:
- 在这篇文章中,我将谈论我一直在研究的个人学习项目。期待一个半熟但有趣的项目,以及关于使用Web框架有趣的讨论。
2.足够的介绍 - 向我展示代码。
简短说明首先:
kamiq是一个打字稿框架,用于构建使用重型装饰器使用的服务器端应用程序。它结合了面向对象和功能性编程方法,以实现其最小的语法设计。 kamiq建立在Express.js之上,设计提供了与Express的高互操作性,使用户能够轻松地通过其现有Express.js代码(包括路线,Middlewares等)移植。
我不会将这个项目视为我想努力完成或准备好生产的项目 - 它只是一个学习项目,并且以代码库格式对节点Web框架进行了讨论。我总是愿意学习新知识,学习的最佳方法之一是重新发明轮子,这就是我在这里做的。
以下脚本涵盖了项目的最重要部分,并且不涵盖更具体的功能和可能性。查看GitHub存储库上的完整读数(末尾链接)。
对,让我们开始。
2.1。配置服务器
要配置服务器,请从服务器类实例化对象。使用公共功能设置您的配置对象和任何其他属性。
import "reflect-metadata";
import { Server } from "kamiq";
import { DefaultErrorHandler, DefaultRequestLogger } from "kamiq/middlewares";
import { SampleController } from "./controllers/sampleController";
const server = new Server();
server.setPort(8001);
server.useJsonBodyParser(true);
server.useController(SampleController);
server.useCors(true);
server.setGlobalRequestLogger(new DefaultRequestLogger());
server.setGlobalErrorHandler(new DefaultErrorHandler(true));
server.start();
2.2。例子
这是控制器的样子:
import { BaseController } from "kamiq";
import { Middleware, Post, Req, Res } from "kamiq/decorators";
import { MySampleMiddleware } from "../middlewares/sampleMiddleware.middleware";
import { MySampleMiddleware2 } from "../middlewares/sampleMiddleware2.middleware";
export class SampleController extends BaseController {
path = "/users"; // Base path for the following routes.
@Guard(new AgencyAuthorizer(), {
ignore: true, // Optional way to ignore a guard (or middleware)
}) // Guards
@Middleware(new LogSignInEvent("user")) // Middlewares
@Post("/siginin") // Get controller registeres the route with a GET method and handles errors
signIn(
@Req() req: Request,
@Res() res: Response,
@Body() body: IUserSignIn,
@Param("userId") userId: string
) {
const { password } = body;
const signIn = AuthService.signIn(userId, password); // Kamiq operation
if (signIn.error) throw new AuthorizationError(signIn.error); // Picked up by global err handling middleware
res.json({ msg: "success" });
}
}
现在让我们刹车:
2.3。控制器和路线
2.3.1。控制器
控制器是扩展BaseController
类的类。在每个控制器类中,您提供path
属性 - 类中定义的所有路由的路由前缀。
3.3.2。路线
每条路线都是带有您选择的名称的函数,将装饰器与HTTP方法名称连接到了:@Get()
用于GET方法,@Post()
用于邮寄等。HTTPDecorator与处理程序功能结合使用,形成了路线。 HTTP方法装饰器采用的字符串参数是路由特定后缀。请注意,这也通过将参数添加到路由中来支持动态路由,如上所示,在:userId
中所示。
2.3.3。参数
路由处理程序本身支持多个装饰器,您可以注入这些装饰器,以访问请求生命周期的各种属性。由于Kamiq使用Express.js来处理HTTP请求,因此您可以将Express Request
和Response
对象注入柄中,如上图所示,使用@Req()
和@Res()
Decorator。另外,您可以访问@Body
,@Query()
,@Param()
,从请求中提取一些特定数据。
2.3.4。中间
kamiq还支持中间Wares,您可以使用@Middleware
装饰器将其连接到路线上。 MiddleWares是扩展KamiqMiddleware
接口的类,该界面与Nest的界面非常相似,其中use()
函数实际上只是一个Vanilla Express.js中间件功能。
这也具有很好的副作用,可以传递中间件功能可以改变其行为。考虑中间件如图:
export class MySampleMiddleware implements KamiqMiddleware {
private readonly someValue: boolean;
constructor(someValue: boolean) {
this.someValue = someValue;
}
async use(req: Request, res: Response, next: NextFunction): Promise<void> {
// Can use someValue to change behavior...
next()
}
}
// Route
@Middleware(new MySampleMiddleware(false))
@Middleware(new MySampleMiddleware2())
@Post('/users')
createUser(@Req() req: Request, @Res() res: Response) {
// @ts-ignore
res.send('user created.')
}
请注意,如果一条路线附有多个中间件,则尊重执行顺序。
MiddleWares还接受第二个参数,即options
对象,您可以在其中指定中间件执行指令,例如在使用ignore
属性测试时绕过执行的能力。
2.3.5。守卫
守卫是一种特殊类型的中间人类型,仅通过授权和身份验证逻辑来遵循单一责任规则。他们实现了类似于中间件接口的接口,但它们返回一个boolean
值,对应于成功或失败的操作,其中成功的操作意味着该请求将继续使用其生命周期中的下一个中间件,并且失败导致身份验证错误(或您可以定义的自定义错误)。
2.4。错误处理
路由中的错误处理在服务器启动中注册的方式隐藏了。注册路线时,它们被包裹在requestErrorHandler
中间件中,该中间件将路由处理程序包裹在试/捕获块中。这样可以确保将被应用程序或用户丢弃的任何错误。
现在已经解决了捕获错误,但我们仍然需要处理它们。 KAMIQ提供了一个公共功能,您可以在称为setGlobalErrorHandler
的服务器对象上访问,该功能将参加实现KamiqErrorMiddleware
接口的类。这是一个明确错误中间件的包装器,您可以将其传递给将注册中间件并处理所有捕获的错误的功能。
kamiq提供了您可以使用的默认defaultErrorHandler
中间件 - 或当然可以轻松编写自己的。
包含在扩展节点的Error
的软件包中包含一个BaseError
类,这使得编写自己的自定义错误很琐碎。只需扩展BaseError
类,添加您自己的逻辑,并且由于所有路由处理程序都包裹在尝试/捕获块中,只需将您的自定义错误扔在处理程序中的任何地方:
@Get('/test')
ping(@Res() res: Response) {
throw new CustomAuthError("my error message!")
res.send('success')
}
和kamiq将为您处理一切。
2.5。操作
操作是一种特殊的功能类型,可以在代码库中使用层间通信进行辅助。让我们考虑一个简单的后端体系结构,由三层组成:演示,服务和数据访问层。
应该在控制器级别上处理错误,从而使图层之间的通信麻烦,考虑到任何逻辑可能会出错。这也提出了一个问题,即如何处理子控制器级别的用户访问的错误。
任何一般功能都可以通过将Operation()
装饰器连接到它。
操作功能是包裹在try/catch块中并具有OperationResult
返回类型的一般函数:
export type OperationResult<T> =
| { success: true; data: T }
| { success: false; error: Error };
这确保任何Operation
函数将返回一个对象,如果操作成功,则将success
属性与data
属性一起返回true
。如果发生故障,success
将是false
,将返回error
属性,使结果的接收者有条件处理错误非常简单:
// Operation function
class MyService {
@Operation
static myOperationFunction() {
throw new Error("oops!");
}
}
// Operation receiver
function mockControllerFunction() {
const operationResult = MyService.myOperationFunction();
// handling the result:
if (operationResult.error) {
// handle error case (throw the error and it will be caught)
}
const result = operationResult.success;
// continue...
}
提示:将服务级别的功能与控制器级别的错误处理一起创建Bulletproof Controller-Service-Service逻辑。
2.6。 kamiq错误
kamiq提供了描述性的框架级别错误处理。如果您在框架级别上进行任何配置或定义错误,Kamiq将丢弃其自定义错误,以帮助您解决问题。这是抛出此类错误的一个示例,由于错误配置的port
变量而被调用:
InvalidArgumentError:
┌ Kamiq encountered an error! ────────────────────────────────────────────┐
│ │
│ │
│ InvalidArgumentError: │
│ Port must be a number between 1 and 65535. Provided port is 5236203 │
│ │
│ Suggestion: Please check your server configuration. │
│ │
│ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
到目前为止,只有几个错误,但是可以轻松添加更多检查。
3.这是回购
4.还有很多事情要做
没什么,但这是诚实的工作,对吗?
我想添加很多功能,我想修复许多错误。缺少许多重要的功能,例如:
- 处理饼干
- 处理内容类型
- 文件上传和管理
- 监视
- 输入验证和消毒
- 渲染模板
- 会议
和更多。
5。
感谢您阅读。我想听听您对这个项目的看法,迫不及待地想从您的想法和讨论中学习。查看存储库,并随意表达您的意见。我知道我做了很多错误,但是改进这就是为什么我首先要分享这一点的原因。享受!
Trina Snow的封面图像(重新发明了方向盘?)