swagger生成angular客户端代码与集成
项目依赖
- angular: 7.0.0
- rxjs: 6.3.3
- angular/cli: 7.0.2
- ng-swagger-gen: 1.4.0
- ng-zorro-antd: 1.7.1
背景
后端使用ABP框架,前端使用angular,根据swagger.json生成客户端api调用代码。swagger官方的代码生成器目前只兼容到angular5,因此使用npm包 [ng-swagger-gen][1],由于swagger.json并未包含ABP框架返回的默认对象这一层,导致客户端代码报错,因此留下此文章记录解决办法。
解决思路
swagger.json是一份遵循了JSON Schema规范的Restful API描述文档,遵循此规范,将ABP框架默认返回的这一层加到swagger.json里即可解决问题。
解决方法
步骤一: 使用命令npm install ng-swagger-gen --save-dev
安装依赖到当前项目。
步骤二: 在项目根目录创建文件 generateApi.js
,此为一个node服务,该服务将远端swagger.json请求到本地之后,按照规范加一层ABP框架默认对象,写入项目根目录,执行swagger-code-gen
,在app
文件夹下生成api
文件夹,即为客户端所需api调用代码。关于更多代码生成自定义选项,请参考 [ng-swagger-gen][1]文档。
[1]: https://github.com/cyclosproject/ng-swagger-gen "[ng-swagger-gen][1]"
generateApi.js
详细代码:
var request = require("request");
var fs = require("fs");
var path = require("path");
var cprocess = require("child_process");
var cwd = process.cwd(); //当前工作目录
var customPath = path.resolve(cwd, "node_modules/\.bin/"); //swagger-code-gen路径,拼接执行命令
//swagger.json地址,替换成你项目swagger.json地址
var swaggerJsonPath = "http://xxx.xxx.x.xxx:xx/swagger/v1/swagger.json";
request(swaggerJsonPath, function (err, response, body) {
var json = JSON.parse(body); //将swagger.json转换成js对象
var paths = json["paths"]; //获取api部分
for (const key in paths) { //每个api拥有一个方法
var url = paths[key];
for (const method in url) {//获取该api请求方法
var schema = url[method]["responses"]["200"]["schema"]; //该方法200 schema
if (schema) {//如果有 schema,将schema按照规范重写
paths[key][method]["responses"]["200"]["schema"] = { //加上ABP默认对象这一层
type: "object",
properties: {
error:"",
result: {
type: "string",
$ref: schema["$ref"]//将原来的DTO赋值给result
},
success: {
type: "boolean"
},
targetUrl: {
type:"string"
},
unAuthorizedRequest: {
type: "boolean"
},
__abp: {
type: "boolean"
}
}
}
}
}
}
//将重写之后的swagger.json,写入到项目根目录
fs.writeFileSync(path.resolve(cwd, "swagger.json"), JSON.stringify(json));
//执行swagger-code-gen生成api代码
cprocess.exec(customPath + "/ng-swagger-gen -i swagger.json", function (error, stdout, stderr) {
if (error !== null) {
console.log('exec error: ' + error.toString());
}
})
})
此时在命令行执行 node generateApi
命令,在你的app文件夹下多了一个api文件夹,这就是依据swagger.json生成的客户端代码了(如果此处出现错误,请检查你是否安装了swagger-code-gen
依赖)。但是查看api-configuration.ts
文件,默认的rootURL为"/",这并不能适配我们的应用。为了不在每次重新生成代码的时候手动修改此参数,接下来我们对此参数进行重置。
步骤三: 重置swagger-code-gen
生成的代码中api调用使用的rootUrl。方法:在app文件夹下添加initApiConfiguration.ts
文件与baseUrl.ts
。
baseUrl.ts
详细代码:
//此文件中导出了所有的项目中使用的完整路径的域名部分,针对不同的环境切换此文件配置即可
//开发环境
export const baseUrl = "http://192.168.6.150:6001";
//生产环境
// export const baseUrl = "http://192.168.6.150:6100";
initApiConfiguration.ts
详细代码:
//swagger生成的代码rooturl配置文件
import { ApiConfiguration } from "./api/api-configuration";
import { Provider, APP_INITIALIZER } from "@angular/core";
import { baseUrl } from "./baseurl";
export function initApiConfiguration(config: ApiConfiguration): Function {
return () => {
config.rootUrl = baseUrl;//将当前环境的域名部分重新赋值给swagger代码
};
}
export const INIT_API_CONFIGURATION: Provider = {
provide: APP_INITIALIZER, //angular初始化时调用
useFactory: initApiConfiguration,
deps: [ApiConfiguration],
multi: true
};
app.module.ts
引入当前设置:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule} from '@angular/core';
import { ApiModule } from "./api/api.module";
import { INIT_API_CONFIGURATION } from "./initApiConfiguration";
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
ApiModule
],
providers: [
INIT_API_CONFIGURATION //初始化调用
],
bootstrap: [AppComponent]
})
export class AppModule { }
步骤四: 创建http拦截器,使用命令ng g service services/api-interceptor
生成api-interceptor.service.ts
服务。此步骤将在每次发起http调用时拦截http,执行此服务中的逻辑。
api-interceptor.service.ts
详细代码:
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from "@angular/common/http";
import { Observable } from "rxjs";
import { catchError } from "rxjs/operators";
import { throwError } from "rxjs"
@Injectable({
providedIn: 'root'
})
export class ApiInterceptorService implements HttpInterceptor {
headers: {}; //httpHeaders
//headers加入jwt token,可在登陆成功时调用此方法,以后每次http请求将设置权限httpHeader
setAuthorizationHeader(token) {
this.headers = {
'Authorization': "Bearer " + token
}
}
// 在用户登出时调用此方法,清空当前应用权限headers
clearAuthorizationHeader(){
this.headers = {};
}
//拦截器代码
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
req = req.clone({
setHeaders: this.headers
});
//错误全局处理
return next.handle(req).pipe(
catchError(this.handleError) //捕获到错误之后,抛给handleError方法处理
);
}
//此为错误集中处理方法,可将具体逻辑写入到此方法
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// 客户端错误
console.error('An error occurred:', error.error.message);
} else {
// 后端返回错误,将错误状态码以及错误信息写入控制台
console.error(`后端返回错误码: ${error.status}, ` + `错误详情为: ${error.error.error.message}`);
}
// 将后端描述的错误详情抛给用户,调用api方法时,需传递error处理方法,参数即为错误信息,string类型
return throwError(error.error.error.message);
};
}
步骤五: app.moudle.ts
引入设置:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Provider, forwardRef} from '@angular/core';
import { ApiModule } from "./api/api.module";
import { INIT_API_CONFIGURATION } from "./initApiConfiguration";
import { HTTP_INTERCEPTORS } from "@angular/common/http";
import { ApiInterceptorService } from "./services/api-interceptor.service";
import { AppComponent } from './app.component';
export const API_INTERCEPTOR_PROVIDER: Provider = {
provide: HTTP_INTERCEPTORS,
useExisting: forwardRef(() => ApiInterceptorService),
multi: true
};
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
ApiModule
],
providers: [
INIT_API_CONFIGURATION,
API_INTERCEPTOR_PROVIDER,
ApiInterceptorService
],
bootstrap: [AppComponent]
})
export class AppModule { }
步骤六: 此时你可以通过命令行执行命令 node generateApi
随心所欲的生成客户端代码了,但是为了更方便,我们可以写一个bat文件到项目根目录,例如generateApi.bat
文件。文件内容如下:
node generateApi
以后后端通知修改了api,如果觉得命令行的方式太麻烦的话,只需要双击这个bat文件就可以一键生成客户端代码了。
注意事项
将api文件夹以及生成的swagger.json写入.gitignore文件,原因是此文件多变,避免冲突。代码如下:
/src/app/api
/swagger.json
总结
此篇文档介绍了如何使用ng-swagger-gen
生成angular客户端代码,并且设置了http拦截器,为每次api调用设置权限httpHeader。同时引入baseUrl.ts
配置文件,将环境切换分离出来。
网友评论