一个轻量级的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
是根据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-Type
为application/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");
MIT