1 | # sm2tsservice
|
2 |
|
3 | ## start
|
4 |
|
5 | ```shell
|
6 | cd yourProjectName
|
7 | java -version
|
8 | npm i sm2tsservice -D
|
9 | ```
|
10 |
|
11 | ## config
|
12 |
|
13 | edit `json2service.json`,也可用 `xxx.js`,然后配置 `-c xxx.js`
|
14 |
|
15 | | 参数 | 值 | 说明 |
|
16 | | ---------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
17 | | url | url 或者文件地址 | swagger、yapi 文档 url 地址或者文件目录,注意:如果是本地文件,文件名不能以 http 开头 |
|
18 | | requestConfig | | 拉取远程文档配置 |
|
19 | | | url | 同 url |
|
20 | | | headers | 请求头配置,见 [headers](https://github.com/request/request#custom-http-headers) |
|
21 | | type | yapi、swagger | 标记类型,默认是 swagger |
|
22 | | swaggerParser | | swagger-code-gen 配置 |
|
23 | | | -o | 输出 typescript 代码目录,默认是当前 src/services |
|
24 | | | -t | 模板目录,默认是工具内置模板目录 plugins/typescript-tkit/,避免修改 |
|
25 | | | -l | 模板目录,默认是 typescript-angularjs,避免修改 |
|
26 | | validateResponse | boolean | 是否生成校验逻辑,默认 false,[详细文档](./src/validate/README.md) |
|
27 | | yapiConfig | | yapi 相关配置 |
|
28 | | | required | 当直接使用 yapi json 定义返回数据格式的时候,生成的 typescript 文件,默认情况下,所有字段都是可选的,配置成 true,则所有字段都是不可缺省的 |
|
29 | | | bodyJsonRequired | 当直接使用 yapi json 定义 json 格式 body 参数的时候,生成的 typescript 文件,默认情况下,所有字段都是可选的,配置成 true,则所有字段都是不可缺省的 |
|
30 | | | categoryMap | 对象,yapi 项目接口分类中英文映射,如 `{ "公共分类": "Common" }` |
|
31 | | guardConfig | mode | 缺省, safe, strict |
|
32 | | | methodUrl2OperationIdMap | 对象,http method + url => operationId 映射,如 `{"get /api/xxx/xxx": "operationId"}` |
|
33 | | | badParamsReg | 非法参数格式校验正则,默认 `/[^a-z0-9_.[]$]/gi`,仅配置文件是 `*.js` 可用 |
|
34 | | | prefixReg | 生成 `url + Using + http method` 时,需移除 url 前缀正则,默认是 `/^(\/)?api\//g` ,仅配置文件是 `*.js` 可用 |
|
35 |
|
36 | ```json
|
37 | {
|
38 | "url": "./api.json", // 文件路径或url
|
39 | "requestConfig": {
|
40 | "url": "./api.json" // 文件路径或url
|
41 | // 以下所有 request 支持的参数
|
42 | // headers?: Headers;
|
43 | // baseUrl?: string;
|
44 | // callback?: RequestCallback;
|
45 | // jar?: CookieJar | boolean;
|
46 | // formData?: { [key: string]: any };
|
47 | // form?: { [key: string]: any } | string;
|
48 | // auth?: AuthOptions;
|
49 | // oauth?: OAuthOptions;
|
50 | // aws?: AWSOptions;
|
51 | // hawk?: HawkOptions;
|
52 | // qs?: any;
|
53 | // qsStringifyOptions?: any;
|
54 | // qsParseOptions?: any;
|
55 | // json?: any;
|
56 | // jsonReviver?: (key: string, value: any) => any;
|
57 | // jsonReplacer?: (key: string, value: any) => any;
|
58 | // multipart?: RequestPart[] | Multipart;
|
59 | // agent?: http.Agent | https.Agent;
|
60 | // agentOptions?: http.AgentOptions | https.AgentOptions;
|
61 | // agentClass?: any;
|
62 | // forever?: any;
|
63 | // host?: string;
|
64 | // port?: number;
|
65 | // method?: string;
|
66 | // body?: any;
|
67 | // family?: 4 | 6;
|
68 | // followRedirect?: boolean | ((response: http.IncomingMessage) => boolean);
|
69 | // followAllRedirects?: boolean;
|
70 | // followOriginalHttpMethod?: boolean;
|
71 | // maxRedirects?: number;
|
72 | // removeRefererHeader?: boolean;
|
73 | // encoding?: string | null;
|
74 | // pool?: any;
|
75 | // timeout?: number;
|
76 | // localAddress?: string;
|
77 | // proxy?: any;
|
78 | // tunnel?: boolean;
|
79 | // strictSSL?: boolean;
|
80 | // rejectUnauthorized?: boolean;
|
81 | // time?: boolean;
|
82 | // gzip?: boolean;
|
83 | // preambleCRLF?: boolean;
|
84 | // postambleCRLF?: boolean;
|
85 | // withCredentials?: boolean;
|
86 | // key?: Buffer;
|
87 | // cert?: Buffer;
|
88 | // passphrase?: string;
|
89 | // ca?: string | Buffer | string[] | Buffer[];
|
90 | // har?: HttpArchiveRequest;
|
91 | // useQuerystring?: boolean;
|
92 | },
|
93 | "type": "yapi",
|
94 | "yapiConfig": {
|
95 | "required": false,
|
96 | "bodyJsonRequired": false,
|
97 | "categoryMap": {
|
98 | "中文": "English" // yapi 接口分类中英文映射
|
99 | }
|
100 | },
|
101 | "swaggerParser": {
|
102 | "-o": "tmp/services"
|
103 | },
|
104 | "validateResponse": false, // 是否生成校验逻辑
|
105 | "guardConfig": {
|
106 | // + strict 严格模式
|
107 | // - 校验 swagger tags【yapi 接口分类】是否是纯英文
|
108 | // - 方法名使用 http method + url 驼峰形式
|
109 | // - 新项目采用
|
110 | // + safe 安全模式
|
111 | // - 方法名使用 http method + url 驼峰形式
|
112 | // - 老项目升级,不会校验 tags,会生成方法调用替换映射表
|
113 | // + 默认
|
114 | // - http method + url => operationId 映射锁定
|
115 | // - 老项目维持现状
|
116 | "mode": "strict",
|
117 | // swagger 处理重复 operationId 逻辑有风险,因此需要锁定映射关系
|
118 | "methodUrl2OperationIdMap": {
|
119 | "get /api/xxx/xxx": "operationId"
|
120 | }
|
121 | }
|
122 | }
|
123 | ```
|
124 |
|
125 | 参考下方代码,实现 ajax 类【如果使用的 axios,且后端返回数据结构遵循 `{ code?:number;message?:string;result: any }`,可直接复制使用】
|
126 |
|
127 | ```ts
|
128 | import axios, { AxiosError } from 'axios';
|
129 | import qs from 'qs';
|
130 |
|
131 | const inst = axios.create({
|
132 | timeout: 2000,
|
133 | withCredentials: true,
|
134 | headers: {}
|
135 | });
|
136 |
|
137 | // @cc: 检测 axios 响应状态
|
138 | function onStatusError(error: AxiosError | Error) {
|
139 | const err =
|
140 | 'response' in error && error.response
|
141 | ? {
|
142 | code: error.response.status,
|
143 | message: error.response.statusText
|
144 | }
|
145 | : { code: 10001, message: error.message };
|
146 | if (err.code === 401 || err.code === 403) {
|
147 | // @todo 未登录未授权
|
148 | // EventCenter.emit('common.user.status', err);
|
149 | }
|
150 | return err;
|
151 | }
|
152 |
|
153 | export type AjaxPromise<R> = Promise<R>;
|
154 |
|
155 | export interface ExtraFetchParams {
|
156 | extra?: any;
|
157 | }
|
158 |
|
159 | export interface WrappedFetchParams extends ExtraFetchParams {
|
160 | method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'OPTIONS' | 'PATCH' | 'HEAD';
|
161 | url: string;
|
162 | data?: any; // post json
|
163 | form?: any; // post form
|
164 | query?: any;
|
165 | header?: any;
|
166 | path?: any;
|
167 | }
|
168 |
|
169 | export class WrappedFetch {
|
170 | /**
|
171 | * @description ajax 方法
|
172 | */
|
173 | public ajax(
|
174 | { method, url, data, form, query, header, extra }: WrappedFetchParams,
|
175 | path?: string,
|
176 | basePath?: string
|
177 | ) {
|
178 | let config = {
|
179 | ...extra,
|
180 | method: method.toLocaleLowerCase(),
|
181 | headers: { ...header }
|
182 | };
|
183 | // json
|
184 | if (data) {
|
185 | config = {
|
186 | ...config,
|
187 | headers: {
|
188 | ...config.headers,
|
189 | 'Content-Type': 'application/json'
|
190 | },
|
191 | data
|
192 | };
|
193 | }
|
194 | // form
|
195 | if (form) {
|
196 | config = {
|
197 | ...config,
|
198 | headers: {
|
199 | ...config.headers,
|
200 | 'Content-Type': 'application/x-www-form-urlencoded'
|
201 | },
|
202 | data: qs.stringify(form)
|
203 | };
|
204 | }
|
205 | return inst
|
206 | .request({ ...config, url, params: query })
|
207 | .then(res => res.data)
|
208 | .catch(onStatusError);
|
209 | }
|
210 |
|
211 | /**
|
212 | * @description 接口传参校验
|
213 | */
|
214 | public check<V>(value: V, name: string) {
|
215 | if (value === null || value === undefined) {
|
216 | const msg = `[ERROR PARAMS]: ${name} can't be null or undefined`;
|
217 | // 非生产环境,直接抛出错误
|
218 | if (process.env.NODE_ENV === 'development') {
|
219 | throw Error(msg);
|
220 | }
|
221 | }
|
222 | }
|
223 | }
|
224 |
|
225 | export default new WrappedFetch();
|
226 | ```
|
227 |
|
228 | 配置 tsconfig.json[如未使用 ts-loader,还需要配置 webpack alias]
|
229 |
|
230 | ```json
|
231 | {
|
232 | "paths": {
|
233 | "@ajax": ["你的实现文件地址"]
|
234 | }
|
235 | }
|
236 | ```
|
237 |
|
238 | ```shell
|
239 | ./node_modules/.bin/service # 使用默认配置
|
240 | ./node_modules/.bin/service -c config.json # 指定配置文件
|
241 | ./node_modules/.bin/service --clear # 清空上次生成产物
|
242 | ```
|
243 |
|
244 | 或可以写入 `package.json`,通过 `npm run api` 使用
|
245 |
|
246 | ```json
|
247 | {
|
248 | "scripts": {
|
249 | "api": "service --clear"
|
250 | }
|
251 | }
|
252 | ```
|