Skip to content
This repository has been archived by the owner on Oct 13, 2023. It is now read-only.

Latest commit

 

History

History
414 lines (314 loc) · 7.04 KB

README.md

File metadata and controls

414 lines (314 loc) · 7.04 KB

logo

Tskoa github ci coverage

一个轻量级的koa开发框架,用于快速搭建restful api。

开始

创建一个简单的项目结构:

├── package.json
├── src
│   ├── controllers
│   │   └── home.controller.ts
│   └── index.ts
└── tsconfig.json

安装依赖:

npm install tskoa --save

推荐使用typescript:

npm install typescript -g

配置tsconfig.json:

{
    "compilerOptions": {
        "target": "ESNext",
        "module": "CommonJS",
        "sourceMap": true,
        "outDir": "./run",
        "noEmit": false,
        "moduleResolution": "node",
        "experimentalDecorators": true,
        "skipLibCheck": true,
        "pretty": true,
        "strict": true,
        "typeRoots": [
            "./node_modules/@types"
        ]
    },
    "include": [
        "./src/**/*",
    ]
}

src/controller/home.controller.ts创建一个Controller

import { Controller, Get, Route } from "tskoa";

@Route
export class HomeController extends Controller {
    
    @Get
    index() {
        return "Hello world";
    }
}

src/index.ts启动项目:

import { Tskoa } from "tskoa";

import { HomeController } from "./controllers/home.controller";

const app = new Tskoa({
    controllers: [
        HomeController
    ]
});

app.listen(3001);

编译:

tsc -p tsconfig.json  --watch

执行:

node run

结果:

curl http://127.0.0.1:3001/home/index
Hello world

Content-Type

content-type是根据Controller的方法返回值确定的,比如上面的例子,index返回了一个字符串,那么response为:

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 11
Date: Sat, 04 Jul 2020 06:08:00 GMT
Connection: keep-alive

Hello world

你可以返回一个json或者array,这时候Content-Typeapplication/json

@Route
export class HomeController extends Controller {
    
    @Get
    index() {
        return ["Hello world"];
    }
}

结果:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 15
Date: Sat, 04 Jul 2020 15:02:28 GMT
Connection: keep-alive

["Hello world"]

路由

默认情况下,路由是根据Controller类和方法的名称匹配的,例如:

// 匹配 Get /home/index

@Route
export class HomeController extends Controller {
    
    @Get
    index() {
        return "Hello world";
    }
}

当然,你可以在装饰器内传递一个字符串,改变这一情况:

// 匹配 Get /foo/bar

@Route("/foo")
export class HomeController extends Controller {
    
    @Get("/bar")
    async index() {
        return "Hello world";
    }
}

目前有下面几个常用的请求方法装饰器:

  • @Get
  • @Post
  • @Delete
  • @Put
  • @Patch

一个url同时支持多个请求方法是允许的:

// 匹配 Get  /home/index
// 匹配 Post /home/index

@Route
export class HomeController extends Controller {
    
    @Get
    @Post
    index() {
        return "Hello world";
    }
}

路由参数

目前有下面几个常用的路由参数装饰器:

  • @Params
  • @Query
  • @Body
  • @Jwt

命名路由可以通过@Params获得

class User { id: string; }

@Route
export class UserController extends Controller {
    
    @Get("/info/:id")
    async getInfoById(@Params params: User) {
        return { code: 200, data: { id: params.id } };
    }
}

请求get /user/info/1返回:

{
    "code": 200,
    "data": {
        "id": "1"
    }
}

可以使用class-validator,给@Params传递一个参数,进行DTO数据验证:

import { IsNumberString } from "class-validator";

class User {
    @IsNumberString()
    id: string;
}

@Route
export class UserController extends Controller {
    
    @Get("/info/:id")
    async getInfoById(@Params(User) params: User) {
        return { code: 200, data: { id: params.id } };
    }
}

请求get /user/info/abc返回:

{
    "code": -1,
    "message": "id must be a number string"
}

@Query可以获得查询参数,@Body可以获得post body

class User {
    @IsNumberString()
    id: string;
}

class Type {
    @IsString()
    type: string
}

class Content {
    @IsString()
    search: string
}

@Route
export class UserController extends Controller {
    
    @Post("/info/:id")
    async getInfoById(@Params(User) params: User, @Query(Type) query: Type, @Body(Content) body: Content) {
        return {
            code: 200,
            data: {
                id: params.id,
                type: query.type,
                search: body.search
            }
        };
    }
}

发送请求post /user/info/1?type=abc:

POST http://127.0.0.1:3000/user/info/1?type=abc HTTP/1.1
Content-Type: application/json
Accept: */*
Host: 127.0.0.1:3000
Content-Length: 24

{ "search" : "key" }

返回:

{
    "code": 200,
    "data": {
        "id": "1",
        "type": "abc",
        "search": "key"
    }
}

权限验证

@Authorized可以为Controller或者方法开启JWT验证:

import jsonwebtoken from 'jsonwebtoken';

@Route
export class UserController extends Controller {

    @Get
    async token() {
        const token = jsonwebtoken.sign({ user: 'jack' }, 'private key', { expiresIn: '10h' });
        return { code: 200, data: token };
    }

    @Get
    @Authorized
    async info(@Jwt jwt: any) {
        return { code: 200, data: { name: jwt.user } };
    }
}

const app = new Tskoa({
    controllers: [
        UserController
    ],
    jwt: {
        secret: "private key"
    }
});

app.listen(3000);

请求get /user/token获得一个token:

{
    "code": 200,
    "data": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiamFjayIsImlhdCI6MTU5Mzg3MTA3NywiZXhwIjoxNTkzOTA3MDc3fQ.fSiIgZR0-C2D-TAhF5WO4XKEvKavAJ5Lklsj9sH_l0g"
}

请求get /user/info

GET http://127.0.0.1:3000/user/info HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiamFjayIsImlhdCI6MTU5Mzg3MTA3NywiZXhwIjoxNTkzOTA3MDc3fQ.fSiIgZR0-C2D-TAhF5WO4XKEvKavAJ5Lklsj9sH_l0g
Accept: */*
Host: 127.0.0.1:3000

返回:

{
    "code": 200,
    "data": {
        "name": "jack"
    }
}

日志

import path from "path";
import { Logger } from 'tskoa';

const logger = new Logger({
    console: true,                                // 是否在控制台显示
    dirname: path.resolve(__dirname, "../logs"),  // 日志存放目录
    filename: 'logs'                              // 日志文件名
});

logger.info("info 123");
logger.warn("warn 456");
logger.error("error 789");

License

MIT