「TodoList」后端-响应处理与数据验证

    科技2022-07-10  119

    后端-响应处理与数据验证

    1、🍉 响应类型2、🍓 成功响应处理3、🍊 错误响应处理3-1、错误捕获处理 4、🍇 验证请求数据4-1、params4-2、query 和 body4-2-1、定义验证类4-2-2、使用验证4-2-3、验证返回格式 5、🍅 其它业务逻辑错误6、🍑 未命中的路由

    1、🍉 响应类型

    成功响应错误响应

    2、🍓 成功响应处理

    成功响应处理比较简单,正如前面规范提到的,根据不同情况返回对应状态码(200、201)和内容。

    3、🍊 错误响应处理

    针对不同的错误,大致可分为如下几个场景:

    服务器错误(500)其它一些业务逻辑错误(422、401、403……)请求路由不存在(404)

    3-1、错误捕获处理

    我们可以通过 koa-ts-controllers 的统一错误处理函数来捕获错误,并同时输出。

    // file: backend/src/app.ts import Koa,{Context} from 'koa'; // ... await bootstrapControllers(app, { router: router, basePath: '/api', versions: [1], controllers: [ __dirname + '/controllers/**/*' ], errorHandler: async (err: any, ctx: Context) => { let status = 500; let body: any = { "statusCode": 500, "error": "Internal Server error", "message": "An internal server error occurred" }; ctx.status = status; ctx.body = body; } }); // ...

    4、🍇 验证请求数据

    许多时候,我们会对请求携带的数据进行一些必要的验证。

    4-1、params

    对于 params 数据,我们可以直接通过 path 规则进行验证。

    // file: backend/src/controllers/Test.ts //... @Get('/user/:id(\\d+)') //字符串正则,数字才可以进来 public async index( @Params() params: {id: number} ) { console.log(params); } // ...

    4-2、query 和 body

    对于 query 和 body 数据,我们可以通过 class-validator 库来进行统一处理。

    4-2-1、定义验证类

    通过类来定义要验证的数据。其中,属性表示要验证的包含数据名称,配合着 class-validator 提交的装饰器来定义数据验证规则。

    class-validator 地址

    // file: backend/src/controllers/Test.ts import {IsNumberString} from 'class-validator'; class GetUsersQuery { @IsNumberString() page: number; }

    4-2-2、使用验证

    把验证类作为要验证的参数(如:query)的类型。当请求该路由以后就会使用验证类对对应的数据进行验证。

    // file: backend/src/controllers/Test.ts import {Controller, Get, Params, Query} from "koa-ts-controllers"; import {IsNumberString} from 'class-validator'; class GetUsersQuery { @IsNumberString() page: number; } //... @Get('/users') public async getUsers( @Query() query: GetUsersQuery ) { console.log(query); } // ...

    为了方便统一管理,我们可以把验证类存放到一个外部文件中,然后进行引入

    存放目录:backend/src/validators/[模块,如:User].ts

    4-2-3、验证返回格式

    如果验证失败,返回如下格式:

    { "data": [ { "field": "page", "violations": { "isNumberString": "page must be a number string" } } ], "isBoom": true, "isServer": false, "output": { "statusCode": 422, "payload": { "statusCode": 422, "error": "Unprocessable Entity", "message": "validation error for argument type: query" }, "headers": {} } }

    为了保证和我们前面定义的响应格式保持一致,我们对这个数据进行一下处理。

    // file: backend/src/app.ts // ... await bootstrapControllers(app, { router: router, basePath: '/api', versions: [1], controllers: [ __dirname + '/controllers/**/*' ], errorHandler: async (err: any, ctx: Context) => { let status = 500; let body: any = { "statusCode": 500, "error": "Internal Server error", "message": "An internal server error occurred" }; if (err.output) { status = err.output.statusCode; body = {...err.output.payload}; if (err.data) { body.errorDetails = err.data; } } ctx.status = status; ctx.body = body; } }); // ...

    5、🍅 其它业务逻辑错误

    有些错误是一些业务逻辑等方面的错误,比如:用户已经被注册,没有该用户等等。

    我们这里会用到一个库 @hapi/Boom ,上面的 class-validator 与 koa-ts-controllers 配合验证中,也是使用了它。听过它提供的一些 API,返回对应 HTTP 状态的响应格式。

    // file: backend/src/controllers/Test.ts import {Controller, Get, Params, Query} from "koa-ts-controllers"; import {IsNumberString} from 'class-validator'; import Boom from '@hapi/Boom'; class GetUsersQuery { @IsNumberString() page: number; } //... @Get('/users') public async getUsers( @Query() query: GetUsersQuery ) { console.log(query); // 不存在该用户 if (false) { throw Boom.notFound('用户不存在', '这是错误的详细描述'); } } // ...

    返回的错误格式如下:

    { "data": "这是错误的详细描述", "isBoom": true, "isServer": false, "output": { "statusCode": 404, "payload": { "statusCode": 404, "error": "Not Found", "message": "用户不存在" }, "headers": {} } }

    通过前面的统一处理,返回统一的错误格式。

    6、🍑 未命中的路由

    最后,我们还要对未命中的路由(不存在的api)进行一个单独的处理。

    // file: backend/src/app.ts // ... import Boom from '@hapi/Boom'; await bootstrapControllers(app, { //... }); router.all('*', async ctx => { throw Boom.notFound(); });

    以上需要注意:

    awaitrouter.all 放在 bootstrapControllers 后面

    否则每次请求都会先处理 '*'。

    Processed: 0.012, SQL: 8