使用 Typescript 自动验证请求的最佳方法是啥?
Posted
技术标签:
【中文标题】使用 Typescript 自动验证请求的最佳方法是啥?【英文标题】:What is the best way to automatically validate requests using Typescript?使用 Typescript 自动验证请求的最佳方法是什么? 【发布时间】:2020-03-10 16:01:34 【问题描述】:我想使用 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
【问题讨论】:
接口在运行时不存在。 Typescript 不提供此功能。 我能想到的最接近的事情是利用 JSON Schema,但这不在 TypeScript 范围内。 Are TypeScript "Interfaces" also input validators? (In other Modules or User Requests)的可能重复 【参考方案1】:typescript 中的接口纯粹是编译时构造。您正在尝试做的是动态检查正文的类型。
您可以使用架构验证器实现您想要的。
【讨论】:
拥有集成 json 模式验证和打字稿定义的东西会非常有用。这样您就不必两次定义“类型”:防止更新时出现错误。【参考方案2】:没有办法做到这一点。接口仅在编译和检查代码时存在,但 TypeScript 没有运行时。
一旦你的 TypeScript 代码被编译,它就会变成普通的 javascript。而且 JavaScript 没有接口的概念,所以无法检查。
您基本上需要编写自己的函数来验证接口,例如,检查对象上是否存在特定字段,或者它是特定类的实例。
【讨论】:
【参考方案3】:是的,我知道接口在运行时不存在。我创建了这个解决方案,它验证我的输入并在我需要时给我输入错误。我认为我的标题有点误导,所以我还将标题更改为“使用 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
认证.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
updateUser.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
这不是最漂亮的解决方案,但我喜欢它自动验证我的请求并且如果我忘记更新我的验证器对象也会给我错误。但是,是的,在运行时获得 Typescript 接口会很棒。
【讨论】:
【参考方案4】:这是我检查属性键的最佳帮手,同时与打字稿集成。有一个变化是可以递归扩展。
function hasKeys<T, K extends string | number | symbol>(
obj: T,
keys: K | K[]
): obj is T & [P in K]: unknown
return Array.isArray(keys) ? keys.every(k => k in obj) : keys in obj;
declare const obj: unknown;
if(typeof obj === "object" && obj !== null)
if("prop" in obj)
// Property 'prop' does not exist on type 'object'.ts(2339)
let x = obj.prop;
if(hasKeys(obj, "prop"))
let x = obj.prop;
if(hasKeys(obj, ["prop1", "prop2"]))
let x = obj.prop1;
let y = obj.prop2;
【讨论】:
【参考方案5】:如果您还维护 openapi 文档,则可以使用 express-openapi-validator 验证请求。它也适用于 fastify 和 Koa
【讨论】:
以上是关于使用 Typescript 自动验证请求的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
从 node.js 和 Typescript 中的动态文件名加载 JSON 的最佳方法是啥?