安装midwayjs
npm init midway
src/configuration.ts
插件包注入使用配置
import { Configuration, App } from '@midwayjs/decorator';
import * as koa from '@midwayjs/koa';
import * as validate from '@midwayjs/validate';
import * as info from '@midwayjs/info';
import { join } from 'path';
// import { DefaultErrorFilter } from './filter/default.filter';
// import { NotFoundFilter } from './filter/notfound.filter';
import { ReportMiddleware } from './middleware/report.middleware';
@Configuration({
imports: [
koa,
validate, // 使用validata 插件库
{
component: info,
enabledEnvironment: ['local'],
},
],
importConfigs: [join(__dirname, './config')],
})
export class ContainerLifeCycle {
@App()
app: koa.Application;
async onReady() {
// add middleware
this.app.useMiddleware([ReportMiddleware]); // 使用自定义中间件
// add filter
// this.app.useFiter([NotFoundFilter, DefaultErrorFilter]);
}
}
控制器&路由
控制器文件一般来说在 src/controller 目录中,我们可以在其中创建控制器文件。Midway 使用 @Controller() 装饰器标注控制器,其中装饰器有一个可选参数,用于进行路由前缀(分组),这样这个控制器下面的所有路由都会带上这个前缀。
import { Inject, Controller, Get, Query } from '@midwayjs/decorator';
import { Context } from '@midwayjs/koa';
import { UserService } from '../service/user.service';
@Controller('/api') // 访问控制器前缀加入 /api/get_user
export class APIController {
@Inject()
ctx: Context;
@Inject()
userService: UserService;
@Get('/get_user') // // 访问控制器前缀加入 /api/get_user
async getUser(@Query('uid') uid) {
const user = await this.userService.getUser({ uid });
return { success: true, message: 'OK', data: user };
}
}
路由
Midway 提供了更多的路由方法装饰器
// src/controller/home.ts
import { Controller, Get } from '@midwayjs/decorator';
@Controller('/')
export class HomeController {
@Get('/')
async home() {
return 'Hello Midwayjs!';
}
@Post('/update')
async updateData() {
return 'This is a post method'
}
}
@Get
、@Post
、@Put()
、@Del()
、@Patch()
、@Options()
、@Head()
和@All()
,表示各自的 HTTP 请求方法。
@All
装饰器比较特殊,表示能接受以上所有类型的 HTTP Method。
你可以将多个路由绑定到同一个方法上
@Get('/')
@Get('/main')
async home() {
return 'Hello Midwayjs!';
}
获取请求参数
装饰器参数约定
Midway 添加了常见的动态取值的装饰器,我们以 @Query 装饰器举例, @Query 装饰器会获取到 URL 中的 Query 参数部分,并将它赋值给函数入参。下面的示例,id 会从路由的 Query 参数上拿,如果 URL 为 /?id=1 ,则 id 的值为 1,同时,这个路由将会返回 User 类型的对象。
@Query
// src/controller/user.ts
import { Controller, Get, Query } from "@midwayjs/decorator";
@Controller('/api/user')
export class UserController {
@Get('/')
async getUser(@Query('id') id: string): Promise<User> {
// xxxx
}
}
@Query 装饰器的有参数,可以传入一个指定的字符串 key,获取对应的值,赋值给入参,如果不传入,则默认返回整个 Query 对象。
// URL = /?id=1
async getUser(@Query('id') id: string) // id = 1
async getUser(@Query() queryData) // {"id": "1"}
@Body() & @Body('key')
// src/controller/user.ts
// POST /user/ HTTP/1.1
// Host: localhost:3000
// Content-Type: application/json; charset=UTF-8
//
// {"uid": "1", "name": "harry"}
import { Controller, Post, Body } from "@midwayjs/decorator";
@Controller('/user')
export class UserController {
@Post('/')
async updateUser(@Body('uid') uid: string): Promise<User> {
// id 等价于 ctx.request.body.uid
}
}
@Body()
// src/controller/user.ts
// POST /user/ HTTP/1.1
// Host: localhost:3000
// Content-Type: application/json; charset=UTF-8
//
// {"uid": "1", "name": "harry"}
import { Controller, Post, Body } from "@midwayjs/decorator";
@Controller('/user')
export class UserController {
@Post('/')
async updateUser(@Body() user: User): Promise<User> {
// user 等价于 ctx.request.body 整个 body 对象
// => output user
// {
// uid: '1',
// name: 'harry',
// }
}
}
@Body()
与@Query()
组合使用
@Post('/')
async updateUser(@Body() user: User, @Query('pageIdx') pageIdx: number): Promise<User> {
// user 从 body 获取
// pageIdx 从 query 获取
}
Router Params
如果路由上使用 :xxx 的格式来声明路由,那么参数可以通过 ctx.params 获取到。
// src/controller/user.ts
// GET /user/1
import { Controller, Get, Inject } from "@midwayjs/decorator";
import { Context } from '@midwayjs/koa';
@Controller('/user')
export class UserController {
@Inject()
ctx: Context;
@Get('/:uid')
async getUser(): Promise<User> {
const params = this.ctx.params;
// {
// uid: '1',
// }
}
}
Header
除了从 URL 和请求 body 上获取参数之外,还有许多参数是通过请求 header 传递的。框架提供了一些辅助属性和方法来获取。
ctx.headers,ctx.header,ctx.request.headers,ctx.request.header:这几个方法是等价的,都是获取整个 header 对象。
ctx.get(name),ctx.request.get(name):获取请求 header 中的一个字段的值,如果这个字段不存在,会返回空字符串。
我们建议用 ctx.get(name) 而不是 ctx.headers[‘name’],因为前者会自动处理大小写。
// src/controller/user.ts
// GET /user/1
import { Controller, Get, Headers } from "@midwayjs/decorator";
@Controller('/user')
export class UserController {
@Get('/:uid')
async getUser(@Headers('cache-control') cacheSetting: string): Promise<User> {
// no-cache
// ...
}
}
// src/controller/user.ts
// GET /user/1
import { Controller, Get, Inject } from "@midwayjs/decorator";
import { Context } from '@midwayjs/koa';
@Controller('/user')
export class UserController {
@Inject()
ctx: Context;
@Get('/:uid')
async getUser(): Promise<User> {
const cacheSetting = this.ctx.get('cache-control');
// no-cache
}
}
Cookie
通过 ctx.cookies,我们可以在 Controller 中便捷、安全的设置和读取 Cookie
import { Inject, Controller, Get, Provide } from '@midwayjs/decorator';
import { Context } from '@midwayjs/koa';
@Controller('/')
export class HomeController {
@Inject()
ctx: Context;
@Get('/')
async home() {
// set cookie
this.ctx.cookies.set('foo', 'bar', { encrypt: true });
// get cookie
this.ctx.cookies.get('foo', { encrypt: true });
}
}
Session
通过 Cookie,我们可以给每一个用户设置一个 Session,用来存储用户身份相关的信息,这份信息会加密后存储在 Cookie 中,实现跨请求的用户身份保持。
框架内置了 Session 插件,给我们提供了 ctx.session 来访问或者修改当前用户 Session 。
import { Inject, Controller, Get, Provide } from '@midwayjs/decorator';
import { Context } from '@midwayjs/koa';
@Controller('/')
export class HomeController {
@Inject()
ctx: Context;
@Get('/')
async home() {
// 获取 Session 上的内容
const userId = this.ctx.session.userId;
const posts = await this.ctx.service.post.fetch(userId);
// 修改 Session 的值
this.ctx.session.visited = ctx.session.visited ? (ctx.session.visited + 1) : 1;
// ...
}
}
Session 的使用方法非常直观,直接读取它或者修改它就可以了,如果要删除它,直接将它赋值为 null
ctx.session = null;
上传的文件 @Files
上传的文件一般使用 multipart/form-data 协议头,由 @Files 装饰器获取,由于上传功能由 upload 组件提供,具体可以参考 upload 组件。
安装依赖
npm i @midwayjs/upload@3 --save
在 configuration 文件中引入组件
import * as upload from '@midwayjs/upload';
@Configuration({
imports: [
// ...other components
upload
],
// ...
})
export class AutoConfiguration {}
在代码中获取上传的文件
@Controller('/')
export class HomeController {
@Inject()
ctx;
@Post('/upload')
async upload(@Files() files, @Fields() fields) {
/*
files = [
{
filename: 'test.pdf', // 文件原名
data: '/var/tmp/xxx.pdf', // mode 为 file 时为服务器临时文件地址
fieldname: 'test1', // 表单 field 名
mimeType: 'application/pdf', // mime
},
{
filename: 'test.pdf', // 文件原名
data: ReadStream, // mode 为 stream 时为服务器临时文件地址
fieldname: 'test2', // 表单 field 名
mimeType: 'application/pdf', // mime
},
// ...file 下支持同时上传多个文件
]
*/
return {
files,
fields
}
}
}
配置upload
// src/config/config.default.ts
import { uploadWhiteList } from '@midwayjs/upload';
export default {
// ...
upload: {
// mode: UploadMode, 默认为file,即上传到服务器临时目录,可以配置为 stream
mode: 'file',
// fileSize: string, 最大上传文件大小,默认为 10mb
fileSize: '10mb',
// whitelist: string[],文件扩展名白名单
whitelist: uploadWhiteList.filter(ext => ext !== '.pdf'), // 可通过 whitelist: () => true 允许所有后缀文件上传
// tmpdir: string,上传的文件临时存储路径
tmpdir: join(tmpdir(), 'midway-upload-files'), // 如果你使用了 file 模式来获取上传的文件,那么上传的文件会存放在您于 config 文件中设置的 upload 组件配置中的 tmpdir 选项指向的文件夹内。
// cleanTimeout: number,上传的文件在临时目录中多久之后自动删除,默认为 5 分钟 你可以通过在配置中使用 cleanTimeout 来控制自动的临时文件清理时间,默认值为 5 * 60 * 1000,即上传的文件于 5 分钟 后自动清理,设置为 0 则视为不开启自动清理功能。
cleanTimeout: 5 * 60 * 1000,
},
}
获取 body & path & ip
@RequestPath() // 获取路径path
@RequestIP() // 获取ip
@Body(‘id’) // 获取body
@Post('/')
async updateUser(
@Body('id') id: string,
@RequestPath() p: string,
@RequestIP() ip: string): Promise<User> {
}