日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

使用class-validator替换Joi包的方法

發布時間:2025/3/8 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用class-validator替换Joi包的方法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

對每個接口的傳入參數進行校驗,是一個Web后端項目的必備功能,有一個npm包叫Joi可以很優雅的完成這個工作,比如這樣子:

const schema = {userId: Joi.string() }; const {error, value} = Joi.validate({ userId: 'a string' }, schema); 復制代碼

我們使用Typescript是希望得到明確的類型定義,減少出錯的可能性。在一個后端項目中,給每個接口定義它的傳入參數結構以及返回結果的結構,是一件很值得做的事情,因為這樣給后續的維護帶來極大的便利。比如這樣子:

export type IFooParam = {userId: string }export type IFooResponse = {name: string }async foo (param: IFooParam): Promise<IFooResponse> {// Your business codereturn {name: 'bar'} } 復制代碼

現在問題就來了,如果傳入參數希望加多一個字段,我們必須得修改2個地方,一個是Joi的校驗,一個是IFooParam類型的定義。有沒有好的辦法解決這個問題呢?

Class-validaotr

有一個npm包叫class-validator, 是采用注解的方式進行校驗,底層使用的是老牌的校驗包validator.js。
這次試用,發現通過一些小包裝,居然做到像Joi一樣優雅的寫法,而且更好用!

定義傳入/返回結構

import {Length, Min, Max} from 'class-validator'export class IRegister {@Length(11)phone: string@Length(2, 10)name: string@Min(18)@Max(50)age: number }class Button {text: string }export class ORegister {/*** user's id*/userId: stringbuttons: Button[] } 復制代碼

這里定義了2個類,IRegister為傳入參數,通過class-validator規定的注解方式做校驗,ORegister為返回結果。

class-validator官方提供的方式還不能直接對一個請求的body進行校驗,它要求必須要是IRegister類的一個對象,所以需要做一些處理。

使用class-transformer做轉化

跟class-validator的作者也開源了另外一個包,叫class-transformer, 可以將一個json轉成指定的類的對象,官方的例子是這樣的:

import {plainToClass} from "class-transformer";let users = plainToClass(User, userJson); // to convert user plain object a single user. also supports arrays 復制代碼

利用這一點,我們寫一個小工具:

import * as classTransformer from 'class-transformer' import {validate} from 'class-validator' import * as lodash from 'lodash'export class ValidateUtil {private static instance: ValidateUtilprivate constructor () {}static getInstance () {return this.instance || (this.instance = new ValidateUtil())}async validate (Clazz, data): Promise<any> {const obj = classTransformer.plainToClass(Clazz, data)const errors = await validate(obj)if (errors.length > 0) {console.info(errors)throw new Error(lodash.values(errors[0].constraints)[0])}return obj} } 復制代碼

這個小工具提供了一個validate方法,第一個參數是一個類定義,第二個是一個json,它先利用class-transformer將json轉成指定類的對象,然后使用class-validator做校驗,如果校驗錯誤將拋出錯誤,否則返回轉化后的對象。

在Controller中使用

有了上面的工具,就可以方便地在代碼中對傳入參數做校驗了,比如這樣:

static async register(ctx) {const iRegister = await ValidateUtil.getInstance().validate(IRegister, ctx.request.body)const oRegister = await UserService.register(iRegister)ctx.body = oRegister} 復制代碼

新問題

到了這里,完美地使用class-validator替換掉了Joi。

但是還有一個問題沒解決,也是之前一直遺留的問題。

我們使用apidoc編寫接口文檔,當新增或修改一個接口時,是通過編寫一段注釋,讓apidoc自動生成html文檔,將文檔地址發給前端,可以減少雙方的頻繁溝通,而且對前端的體驗也是非常好的。比如寫這樣一段注釋:

/*** @api {post} /user/registerOld registerOld* @apiGroup user* @apiName registerOld* @apiParam {String} name user's name* @apiParam {Number} age user's age* @apiSuccess {String} userId user's id */router.post('/user/registerOld', UserController.register) 復制代碼

apidoc會幫我們生成這樣的文檔:

問題比較明顯,當我們要新增一個參數時,需要修改一次類的定義,同時還要修改一次apidoc的注釋,很煩,由于很煩,文檔會慢慢變得沒人維護,新同事就會吐槽沒有文檔或者文檔太舊了。

理想的情況是代碼即文檔,只需要修改類的定義,apidoc文檔自動更新。

探索apidoc根據class-validator的定義生成

從同事的分享中得知一個廢棄的npm包,叫apidoc-plugin-ts, 可以實現根據ts的interface定義來生成apidoc的。官方的例子:

filename: ./employers.tsexport interface Employer {/*** Employer job title*/jobTitle: string;/*** Employer personal details*/personalDetails: {name: string;age: number;} }@apiInterface (./employers.ts) {Person} 復制代碼

會轉化成:

@apiSuccess {String} jobTitle Job title@apiSuccess {Object} personalDetails Empoyer personal details@apiSuccess {String} personalDetails.name@apiSuccess {Number} personalDetails.age 復制代碼

雖然不知道為什么作者要廢棄它,但是它的思想很好,源碼也很有幫助。

給我的啟發是,參考這個npm包,寫一個針對class定義來生成apidoc的插件就行了。

造輪子: apidoc-plugin-class-validator

輪子的制造細節不適合在這里陳述,基本上參考apidoc-plugin-ts,目前已經發布在npm上了,apidoc-plugin-class-validator

使用apidoc-plugin-class-validator

以上面的注冊接口為例,使用方法:

/*** @api {post} /user/register register* @apiGroup user* @apiName register* @apiParamClass (src/user/io/Register.ts) {IRegister}* @apiSuccessClass (src/user/io/Register.ts) {ORegister}*/router.post('/user/register', UserController.register) 復制代碼

就會生成文檔:

后續新增字段,只需修改IRegister類的定義就行,真正做到了修改一處,處處生效,代碼即文檔的效果。

本文的demo代碼在這里,這是一個簡單的web后端項目,看代碼更容易理解。

轉載于:https://juejin.im/post/5c9b3b53f265da612e6d708c

總結

以上是生活随笔為你收集整理的使用class-validator替换Joi包的方法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。