trello02:后端-请求与响应的统一处理

1 获取请求数据

通常客户端的请求会根据业务需求同时发送一些额外数据,数据的传输携带方式也有如下几种常见场景:

  • params
    也就是我们所说的动态路由可变的部分。

  • queryString
    url 问号后的部分。

  • body
    通常就是我们说的请求正文部分。

  • headers
    除了一些内置头,还可以自定义一些头信息,我们应用中的用户授权 token 就是通过头信息来传递的。

koa-ts-controllers 不仅仅提供请求方法装饰器来处理请求,同时也提供了一些 参数装饰器 来处理请求数据。

  • @Params()

  • @Query()

  • @Body()

  • @Header()

1-1 Params

Params 装饰器

@Params() 或者 @Params(name)

我们可以通过装饰器 Params 拿取对应的数据。

// file: backend/src/controllers/Test.ts
import {
    Controller,
    Get,
    Post,
    Params
} from "koa-ts-controllers";

@Controller("/test")
class TestController {
    @Get("/hello")
    async hello() {
        return "Hello Test!"
    }

    @Get("/user/:id")
    async getUser(@Params() p:{id: number}) {
        return "当前params中用户id是:" + p.id;
    }
}

在这里插入图片描述
不传参是获取所有内容,也可以直接将id传参给@params

    @Get("/user/:id")
    async getUser(@Params("id") id: number) {
        return "当前params中用户id是:" + id;
    }

1-2 Query

Query 装饰器的用法与 Params 的用法一致。

    @Get("/user")
    async getUser2(@Query() q: {id: number}) {
        return "当前params中用户id是:" + q.id;
    }

在这里插入图片描述

1-3 Body

同上。

    @Post("/postUser")
    async postUser(@Body() body: {
        name: string,
        pwd: string,
    }) {
        return `提交的数据为:${JSON.stringify(body)}`
    }

注意controllers只起到一个管理职责,并没有实现路由功能
所以需要安装解析body的三方库

npm i koa-bodyparser
npm i -D @types/koa-bodyparser

使用

// app.ts
import koaBodyParser from "koa-bodyparser";
...
app.use(koaBodyParser());

利用postman模拟请求
在这里插入图片描述

1-4 Header

还是同上。

    @Post("/postUser")
    async postUser(
        @Body() body: {
            name: string,
            pwd: string,
        },
        @Header() h: any
    ) {
        console.log(h);
        return `提交的数据为:${JSON.stringify(body)}`
    }

2 数据的响应

2-1 响应类型

  • 成功响应
  • 错误响应

2-2 成功响应处理

成功响应处理比较简单,根据不同情况返回对应状态码(200、201)和内容。

2-3 错误响应处理

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

错误捕获处理

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

// file: backend/src/app.ts

// ...
		await bootstrapControllers(app, {
        router: router,
        basePath: '/api',
        versions: [1],
        controllers: [
            path.resolve(__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;
        }
    });
// ...

在这里插入图片描述

2-4 验证请求数据

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

params

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

比如id只允许数字,非数字报错404

 file: backend/src/controllers/Test.ts

//...
    @Get("/user/:id(\\d+)")
    async getUser(@Params("id") id: number) {
        return "当前params中用户id是:" + id;
    }
// ...

在这里插入图片描述

query 和 body

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

class-validator 地址

定义验证类

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

 file: backend/src/controllers/Test.ts

import {IsNumberString} from 'class-validator';

class GetUsersQuery {
    @IsNumberString()
    page: number;
}
使用验证

把验证类作为要验证的参数(如: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")
    async getUsers(@Query() q: getUsersQuery) {
        console.log(q);
        return `传过来的query:${JSON.stringify(q)}`;
    }
// ...

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

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

验证返回格式

如果验证失败,返回如下格式:
在这里插入图片描述

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

// 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;
        }
    });
// ...
自定义验证信息

class GetUsersQuery {

    // 验证是否为数字字符串
    @IsNumberString({},{
        message: "page必须为数字"
    })
    page: number;
}

在这里插入图片描述

2-5 其它业务逻辑错误

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

我们这里会用到一个库 @hapi/Boom ,上面的 class-validatorkoa-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")
    async getUsers(@Query() q: GetUsersQuery) {
        // 比如业务逻辑错误,用户名已经被注册
        if (true) {
            throw Boom.notFound("注册失败", "用户名已经被注册了");
        }
        return `传过来的query:${JSON.stringify(q)}`;
    }
// ...

返回的错误格式如下:

在这里插入图片描述

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

2-6 未命中的路由

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

// file: backend/src/app.ts
// ...
import Boom from '@hapi/Boom';

await bootstrapControllers(app, {
    //...
});

router.all('*', async ctx => {
    throw Boom.notFound();
});

以上需要注意:

  • await
  • router.all 放在 bootstrapControllers 后面

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

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值