我必须编写 zod express 验证器中间件,它是从 zod-express-middleware
稍作修改的这是我的自定义请求验证器
export type ErrorListItem = {
type: 'Query' | 'Params' | 'Body' | 'Response';
errors: ZodError<any>;
};
type RequestSchemas<TParams, TQuery, TBody, TResponse> = Partial<{
params: ZodSchema<TParams>;
query: ZodSchema<TQuery>;
body: ZodSchema<TBody>;
response: ZodSchema<TResponse>;
}>;
type RequestCallback<TParams, TQuery, TBody, TResponse> = (
data: {
params: TParams;
body: TBody;
query: TQuery;
},
req: Express.Request,
res: Express.Response
) => Promise<TResponse>;
type ValidatedRequest = (
req: Express.Request,
res: Express.Response,
next: Express.NextFunction
) => Promise<void>;
export const doValidatedRequest =
<TParams, TQuery, TBody, TResponse>(
schemas: RequestSchemas<TParams, TQuery, TBody, TResponse>,
cb: RequestCallback<TParams, TQuery, TBody, TResponse>,
succesStatusCode: number = 200
): ValidatedRequest =>
async (req, res, next) => {
try {
const errors: ErrorListItem[] = [];
const requestData = {
params: {} as any,
body: {} as any,
query: {} as any
};
if (schemas.params) {
const parsedParams = schemas.params.safeParse(req.params);
if (parsedParams.success) {
requestData.params = parsedParams.data;
} else {
errors.push({ type: 'Params', errors: parsedParams.error });
}
}
if (schemas.body) {
const parsedBody = schemas.body.safeParse(req.body);
if (parsedBody.success) {
requestData.body = parsedBody.data;
} else {
errors.push({ type: 'Body', errors: parsedBody.error });
}
}
if (schemas.query) {
const parsedQuery = schemas.query.safeParse(req.query);
if (parsedQuery.success) {
requestData.query = parsedQuery.data;
} else {
errors.push({ type: 'Query', errors: parsedQuery.error });
}
}
if (errors.length) {
next(errors);
}
const response = await cb(requestData, req, res);
if (schemas.response) {
const parsedResponse = schemas.response.safeParse(response);
if (parsedResponse.success) {
res.status(succesStatusCode).json(parsedResponse.data);
} else {
errors.push({ type: 'Response', errors: parsedResponse.error });
}
} else {
// Response not specified and thus not expected
res.status(succesStatusCode).json(response);
}
} catch (error) {
next(error);
}
};
现在当我尝试使用它时
router.get('/', doValidatedRequest({}, async ({}, req, res) => someFunction(req, res));
或
router.get('/:id', doValidatedRequest({
query: z.object({
id: z.string()
}),
}, async ({ query: { id }}) => someFunction(id));
我不断收到 eslint 错误
Promise returned in function argument where a void return was expected. eslint(@typescript-eslint/no-misused-promises)
我如何定义
someFunction
似乎并不重要
const someFunction = async (): Promise<void> => { // do something }
const someFunction = async (): Promise<string> => { return "OK" }
const someFunction = async (id: string): Promise<string> => { return id }
const someFunction = (): void => { // do something }
const someFunction = (): string => { return "OK" }
const someFunction = (id: string): string => { return id }
有人能看出这是为什么吗?我似乎找不到原因,而且我也想知道它是否会在某个时候引起任何进一步的问题。我该如何改进这个中间件?
其想法是,这可以用于验证请求参数和响应,如果需要,将经过验证且类型正确的参数传递给控制器函数,然后控制器函数将可选的经过验证的响应传递给验证器,然后验证器将响应发送回请求者.
我的定义似乎有两个问题,都可以在这个片段中看到
type ValidatedRequest = (
req: Express.Request,
res: Express.Response,
next: Express.NextFunction
) => Promise<void>;
export const doValidatedRequest =
<TParams, TQuery, TBody, TResponse>(
schemas: RequestSchemas<TParams, TQuery, TBody, TResponse>,
cb: RequestCallback<TParams, TQuery, TBody, TResponse>,
succesStatusCode: number = 200
): ValidatedRequest =>
async (req, res, next) => {
第一个是我将
ValidatedRequest
的返回类型输入为Promise<void>
,当这是doValidatedRequest
的返回类型时,TS看到有一个Promise<void>
返回。整个 ValidatedRequest
类型可能有点多余,doValidatedRequest
的返回类型可以只是 express.RequestHandler
。
其次,我将
ValidatedRequest
(或 RequestHandler
)函数定义为 async (req, res, next) => ...
,这也告诉 TS 有某种要返回的承诺。
第三,我想我有一些奇怪的 eslint 规则告诉我我不应该在快速中间件中使用承诺。不确定,快递是否可以处理这些。我想应该可以吧
无论如何,现在的解决方法是将
doValidatedRequest
重新定义为
export const doValidatedRequest =
<TParams, TQuery, TBody, TResponse>(
schemas: RequestSchemas<TParams, TQuery, TBody, TResponse>,
cb: RequestCallback<TParams, TQuery, TBody, TResponse>,
succesStatusCode = 200
): RequestHandler =>
(req, res, next) => {
const errors: ErrorListItem[] = [];
const requestData = {
params: {} as any,
body: {} as any,
query: {} as any
};
if (schemas.params) {
const parsedParams = schemas.params.safeParse(req.params);
if (parsedParams.success) {
requestData.params = parsedParams.data;
} else {
errors.push({ type: 'Params', errors: parsedParams.error });
}
}
if (schemas.body) {
const parsedBody = schemas.body.safeParse(req.body);
if (parsedBody.success) {
requestData.body = parsedBody.data;
} else {
errors.push({ type: 'Body', errors: parsedBody.error });
}
}
if (schemas.query) {
const parsedQuery = schemas.query.safeParse(req.query);
if (parsedQuery.success) {
requestData.query = parsedQuery.data;
} else {
errors.push({ type: 'Query', errors: parsedQuery.error });
}
}
if (errors.length) {
next(errors);
}
cb(requestData, req, res)
.then(response => {
if (schemas.response) {
const parsedResponse = schemas.response.safeParse(response);
if (parsedResponse.success) {
res.status(succesStatusCode).json(parsedResponse.data);
} else {
errors.push({ type: 'Response', errors: parsedResponse.error });
}
} else {
// Response not specified and thus not expected
res.status(succesStatusCode).json(response);
}
})
.catch(next);
};
这应该和原来的一样有效,并且没有显示任何类型的 TS/eslint 错误