使用 Typescript 自动验证请求的最佳方法是什么?

M. *_*Hav 7 node.js express typescript

我想使用 Typescript 接口来轻松验证后端请求。有没有办法让它发生?我想做的事情的例子:

import { Request, Response } from 'express'

interface AuthenticateParams {
    email: string
    password: string
}

type AuthenticationRequest = Request & {body: AuthenticateParams}

const authenticate = async (req: AuthenticationRequest, res: Response) => {
    if (typeof req.body !== AuthenticateParams) res.status(400).send('Invalid body')

    // DO STUFF
    let stuff

    res.status(200).send(stuff)
}

export default authenticate
Run Code Online (Sandbox Code Playgroud)

M. *_*Hav 4

2022 年更新:

我偶然发现了Runtypes,我认为这是一个非常酷的解决方案。当您想到它时,这是一个非常明显的问题:您使用中间的解决方案,它会自动为您提供正确的类型并可以检查有效性。这比尝试从类型到验证要好得多,因为接口在运行时并不存在,正如多次所说的那样。

原答案:

是的,我知道接口在运行时不存在。我创建了这个解决方案,它可以验证我的输入并在需要时提供类型错误。我认为我的标题有点误导,所以我还将标题更改为“使用 Typescript 自动验证请求的最佳方法是什么”。

这个想法是迫使我为我希望用来验证后端端点的每个接口编写一个简单的模式。另外,为了最大限度地减少人为错误的可能性,我的 linter 类型检查validatorObject.

validateParams.ts:

const validateParams: (targetObject: any, validatorObject: any) => boolean = (targetObject, validatorObject) => {
    if (typeof targetObject !== typeof validatorObject) return false
    if (typeof targetObject === 'object') {
        let validObject = true
        if (Array.isArray(targetObject)) {
            for (let subObject of targetObject) {
                validObject = validObject && validateParams(subObject, validatorObject[0])
            }
        } else {
            for (let key of Object.keys(validatorObject)) {
                if (typeof targetObject[key] === 'object') validObject = validObject && validateParams(targetObject[key], validatorObject[key])
                if (typeof targetObject[key] !== typeof validatorObject[key]) validObject = false
            }
        }
        return validObject
    }
    return true
}

export default validateParams
Run Code Online (Sandbox Code Playgroud)

验证.ts

import { Request, Response } from 'express'
import validateParams from "./validateParams"

interface AuthenticateParams {
    email: string
    password: string
}

const validatorObject: AuthenticateParams = {
    email: 'string',
    password: 'string'
}

type AuthenticationRequest = Request & {body: AuthenticateParams}

const authenticate = async (req: AuthenticationRequest, res: Response) => {
    if (!validateParams(req.body, validatorObject)) res.status(400).send('Invalid body')

    // DO STUFF
    let stuff

    res.status(200).send(stuff)
}

export default authenticate
Run Code Online (Sandbox Code Playgroud)

更新用户.ts

import { Request, Response } from 'express'
import validateParams from "./validateParams"

interface UpdateUserParams {
    name: string
    products: {
        name: string
        price: number
    }[]
}

const validatorObject: UpdateUserParams = {
    name: 'string',
    products: [{name: 'string', number: 0}]
}

type AuthenticationRequest = Request & {body: UpdateUserParams}

const updateUser = async (req: UpdateUserParams, res: Response) => {
    if (!validateParams(req.body, validatorObject)) res.status(400).send('Invalid body')

    // DO STUFF
    let stuff

    res.status(200).send(stuff)
}

export default updateUser
Run Code Online (Sandbox Code Playgroud)

这不是最漂亮的解决方案,但我喜欢它自动验证我的请求,并且如果我忘记更新我的 validatorObject,也会给我错误。但是,是的,如果能在运行时获得 Typescript 接口那就太棒了。