Swagger 简要介绍
Swagger / Open API 在Restful API 领域已慢慢成为标准,越来越多的系统使用swagger来规范开发接口文档,由于Swagger 本身并不依赖特定的语言和开发平台,所以特别合适作为前后端分离的接口标准来使用。 Swagger 官方提供了两种文档UI形式,一种是 Swagger UI, 另一种是 Swagger Editor.
-
Swagger UI 常常作为项目工程的一部份,很多程序都采用自身的特性,在程序代码上增加注释、注解、装饰器等方式完成swagger 的接口文档自动生成和文档展示。
-
Swagger Editor 提供了一个在线编辑的编辑器,使之能以文档的方式编辑接口,并提供了两块的生成功能:服务端代码与客户端代码,代码生成涵盖了大部分的主流语言与框架非常方便。
直接可用的代码
很多同学可能有疑问,这样生成的代码到底能用吗?
我的回答不仅是能用,而且很好用。 以 ng-alain 为例
代码位置 https://github.com/vellengs/typerx/blob/master/packages/client/src/app/delon.module.ts 定制下provider 的 basePath 设置。
export function apiConfig(): Configuration {
return new Configuration({
basePath: `${location.protocol}//${location.host}`
});
}
注册下模块
ApiModule.forRoot(apiConfig),
然后把目录释放到 https://github.com/vellengs/typerx/tree/master/packages/client/src/generated 这个目录,看清楚,generted目录 是可以一字不改的,如果改了以后需要重新生成岂不是大麻烦?
放置到 angular 项目里后就可以愉快的使用 service了,如下示例: https://github.com/vellengs/typerx/blob/master/packages/client/src/app/pages/system/settings/settings.ts
profileSave(event?) {
const entry: EditProfileDto = Object.assign({}, event);
this.coreService.userUpdate(entry).subscribe((res) => {
if (res) {
this.settings.setUser(res);
this.msg.success('个人资料修改成功');
}
});
}
saveSysSettings(event) {
const entry = Object.assign({}, event);
this.coreService.settingUpdateSettingsByName('main', entry).subscribe((res) => {
if (res) {
this.settingsData = res;
this.msg.success('系统设置更新成功');
}
});
}
load() {
this.coreService.settingGetSettingsByName('main').subscribe((res) => {
if (res) {
this.settingsData = res;
}
});
}
方法变得可以感知了。
自动生成、下载、解压
上一步骤已经可以使用了,但是有点小小的麻烦;每次都要打开 editor.swagger.io 粘贴文档上去,然后去点生成,然后解压,这样是不是不够平滑呢?
那让我们写个脚本吧,自动完成上面的事情。 完整脚本地址
async function loadSwagger() {
/** 读取 swagger.json 接口文档文件 */
const jsonPath = path.resolve(process.cwd(), 'dist', 'swagger.json');
const json = require(jsonPath);
const client = axios.create({
httpsAgent: new https.Agent({
rejectUnauthorized: false
})
});
/**
* 提交 post 到服务器并获得下载链接
*/
const result = await client.post(gateway, { spec: json });
if (result.data && result.data.link) {
const response = await client({
method: 'GET',
url: result.data.link,
responseType: 'stream'
})
const local = path.resolve(process.cwd(), 'swagger.zip');
const generatedFolder = path.resolve(process.cwd(), './../client/src/generated');
const templateFolder = path.resolve(process.cwd(), 'decompress', 'typescript-angular-client');
const decompress = path.resolve(process.cwd(), 'decompress');
// 处理下载压缩包文件,解压并删除临时文件
response.data.pipe(fs.createWriteStream(local)).on('finish', (done: any) => {
fs.createReadStream(local).pipe(unzip.Extract({ path: 'decompress' })).on('close',
async (done: any) => {
fs.unlinkSync(local);
await removeFolder(generatedFolder);
fs.renameSync(templateFolder, generatedFolder);
await removeFolder(decompress);
});
});
}
}
/**
* 若已经生成,则删除文件夹
* @param folder 文件夹
*/
async function removeFolder(folder: string) {
if (fs.existsSync(folder)) {
await new Promise((resolve) => {
rimraf(folder, () => {
resolve(true);
});
})
}
}
loadSwagger();
总共没几行代码,是不是觉得很简单呢,没错要的就是简单,但是很好的完成了我们的工作。
有了脚本之后你只要
ts-node xxxxx/apigen.ts
即可完成工作。
结束语,标准的接口在前后端分离的开发中起到很好的规范作用,而平滑的开发体验也是重要的一环,如果你有疑问,或者有更好的方式,欢迎和作者交流学习 https://github.com/vellengs 首发于掘金