# 使用nodejs + ts + koa搭建服务端脚手架 --- 2. 接口开发与优化
# 响应请求
koa服务器搭建起来之后,就可以请求服务器,并让服务器对请求作出对应的响应。
在上一节中,我们在app.ts,写过这样的代码:
...
app.use(async (ctx: Koa.Context) => {
ctx.body = 'Hello World';
});
...
这其实就是一个最简单的接口,它表示,只要收到请求,就返回Hello World
但是这样的功能还远远不够。
- 解析请求路径,读取query参数
ctx: Koa.Context是koa里的上下文对象,通过ctx.request,就可以获取请求信息
// app.ts
app.use(async (ctx: Koa.Context) => {
const req:Koa.Request = ctx.request
const originalUrl:string = req.originalUrl
const method:string = req.method
const query = req.query
switch (originalUrl){
case 'login':
login(query.userName,query.password)
break;
case 'getUserInfo':
getUserInfo(query.userName)
break;
default
other()
}
});
function login(){
// 登录
}
function getUserInfo(){
// 获取用户信息
}
function other(){
// 其它
ctx.body = '请求错误,无响应路径'
}
我们可以通过originalUrl,获取请求的路径;通过method,获取请求的类型;通过query,获取请求的参数;然后根据不同的路径,进行不同的操作。
- 读取body数据
现在只是能读取query,如果要读取post的body,还需要安装koa-bodyparser
安装依赖:
npm i koa-bodyparser @type/koa-bodyparser
代码:
// app.ts
import Koa from "koa"
import koaBodyParser from "koa-bodyparser"
import http from 'http'
const app = new Koa()
app.use(koaBodyParser())
app.use(async (ctx: Koa.Context) => {
switch (originalUrl){
case 'login':
login(query.userName,query.password)
break;
case 'getUserInfo':
getUserInfo(query.userName)
break;
default
other()
}
});
function login(){
// 登录
}
function getUserInfo(){
// 获取用户信息
}
function other(){
// 其它
ctx.body = '请求错误,无响应路径'
}
const server: http.Server = new http.Server(app.callback())
server.listen(3001,()=> {
console.log('app started at port 3001...')
})
# 优化接口
目前我们的接口已经具备基本的功能,但还远远不够优雅。
理想的情况下,至少应该具备一下功能:
一个ts文件只写一个接口或者一个功能
约定一个文件夹,程序自动读取对应路径下的所有ts文件并创建对应的接口
能够简单地设置接口能响应的请求类型(post还是get等)
统一返回格式,帮助前后端定位错位
请求参数校验
为了实现上述功能,我们首先需要一个路由系统
- 路由系统
上面我们通过switch去判断不同请求路径,并执行不同的方法,其实就是路由系统的雏形,我们可以选择在这个基础上继续去实现和优化代码,但是前辈们已经封装过好用的轮子了--koa-router,可以直接使用。
安装依赖:
npm i koa-router @type/koa-router
在src目录下新建api目录,当前是第一个版本,我们可以在api下新建一个v1,将当前版本的接口都放在v1目录下,然后我们在v1目录下新建第一个接口helloWorld.ts:
// api/v1/helloWorld.ts
import KoaRouter from 'koa-router'
const router = new KoaRouter({
prefix:'/api/v1'
})
router.get('/helloWorld',async (ctx: Koa.Context)=> {
const {
user
} = ctx.query
ctx.body = `Hello world, ${user}`
})
export default router
在src目录下创建common/utils目录,在这里存放通用的工具方法,然后新建Utils.ts。
在Utils.ts里,我们写一个fileDisplay方法,能够遍历对应目录下的所有文件,并引入其导出,最后use导出的接口
// common/utils/utils.ts
import * as fs from 'fs';
import * as path from 'path';
import Koa from 'koa'
class Utils {
/**
* 文件遍历方法
* @param filePath 需要遍历的文件路径
* @param app koa实例
*/
public static async fileDisplay(filePath: string,app: Koa) {
// 根据文件路径读取文件,返回一个文件列表
const files:string[] = await fs.readdirSync(filePath);
// 遍历读取到的文件列表
for(let i = 0;i<files.length;i++){
const fileName = files[i]
// path.join得到当前文件的绝对路径
const _filePath:string = path.join(filePath, fileName);
const stats:fs.Stats = await fs.statSync(_filePath)
const isFile = stats.isFile(); // 是否为文件
const isDir = stats.isDirectory(); // 是否为文件夹
if (isFile) {
const file = require(_filePath)
app.use(file.default.routes())
console.log(`加载接口:${_filePath.replace(process.cwd(),'')}成功`)
}
if (isDir) {
await Utils.fileDisplay(_filePath,app); // 递归,如果是文件夹,就继续遍历该文件夹里面的文件;
}
}
}
}
export default Utils
在src目录下创建core目录,新建Init.ts,这里就是我们的核心控制器,所有的功能包括接口初始化都会在这里完成
import Koa from 'koa'
import path from 'path'
import Utils from '../common/utils/Utils'
import http from 'http'
class Init{
public static app: Koa<Koa.DefaultState, Koa.DefaultContext>
public static server: http.Server
public static initCore(app: Koa<Koa.DefaultState, Koa.DefaultContext>, server: http.Server) {
Init.app = app
Init.server = server
Init.initLoadRouters()
}
static async initLoadRouters() {
const dirPath = path.join(`${process.cwd()}/src/api/`)
await Utils.fileDisplay(dirPath,Init.app)
console.log('接口加载完成')
}
}
export default Init.initCore
最后再在app.ts里引入Init.ts
import Koa from "koa"
import initCore from './core/Init'
import koaBodyParser from "koa-bodyparser"
import http from 'http'
const app = new Koa()
app.use(koaBodyParser())
initCore(app,server)
const server: http.Server = new http.Server(app.callback())
server.listen(3001,()=> {
console.log('app started at port 3001...')
})
- 参数校验
Ajv