node-easywechat 3.0.0-beta.2 → 3.0.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,11 +1,26 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v3.0.0-beta.5 (2022-06-29)
5
+
6
+ - Fix: 修复服务端获取消息异常的问题
7
+
8
+ ## v3.0.0-beta.4 (2022-06-24)
9
+
10
+ - Feat: 请求接口数据增加 FormData 实例的支持
11
+
12
+ ## v3.0.0-beta.3 (2022-06-22)
13
+
14
+ - Fix: 修复获取js ticket时无法自动获取access_token的问题
15
+ - Fix: 修复日志处理回调函数的参数提示异常的问题
16
+
4
17
  ## v3.0.0-beta.2 (2022-06-21)
5
18
 
19
+ - Feat: 统一调用入口增加一些常用的类
20
+
6
21
  - Fix: 修复获取的服务端实例没有代码提示的问题
7
22
 
8
- ## v3.0.0-beta.1, origin/3.x (2022-06-21)
23
+ ## v3.0.0-beta.1 (2022-06-21)
9
24
 
10
25
  - Feat: 新增设置预置参数相关方法
11
26
  - Feat: 增加设置日志处理器的方法
@@ -63,8 +63,7 @@ class MessageMixin {
63
63
  }
64
64
  withBody(body) {
65
65
  if (Buffer.isBuffer(body)) {
66
- this.content = Buffer.from('');
67
- this.content.copy(body);
66
+ this.content = body;
68
67
  }
69
68
  else if (typeof body === 'string') {
70
69
  this.content = Buffer.from(body);
@@ -43,7 +43,7 @@ declare class ServerRequest implements ServerRequestInterface {
43
43
  * @param body 支持 Buffer、object对象、JSON字符串、XML字符串、QueryString格式的 body 内容字符串
44
44
  * @returns
45
45
  */
46
- static createFromIncomingMessage(req: IncomingMessage, body?: Buffer | Record<string, any> | string): ServerRequest;
46
+ static createFromIncomingMessage(req: IncomingMessage, body?: Buffer | Record<string, any> | string): Promise<ServerRequest>;
47
47
  }
48
48
  interface ServerRequest extends MessageMixin, RequestMixin {
49
49
  }
@@ -1,4 +1,13 @@
1
1
  'use strict';
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
13
  };
@@ -124,13 +133,14 @@ class ServerRequest {
124
133
  * @returns
125
134
  */
126
135
  static createFromIncomingMessage(req, body = null) {
127
- let request = new ServerRequest(req.method, req.url, req.headers || {}, body, req.httpVersion, (0, url_1.parse)(req.url, true).query || {});
128
- if (!body) {
129
- (0, raw_body_1.default)(req).then(res => {
136
+ return __awaiter(this, void 0, void 0, function* () {
137
+ let request = new ServerRequest(req.method, req.url, req.headers || {}, body, req.httpVersion);
138
+ if (!body) {
139
+ let res = yield (0, raw_body_1.default)(req);
130
140
  request.withBody(res);
131
- });
132
- }
133
- return request;
141
+ }
142
+ return request;
143
+ });
134
144
  }
135
145
  }
136
146
  ;
@@ -2,6 +2,7 @@ import { AxiosInstance, AxiosRequestConfig, Method } from 'axios';
2
2
  import HttpClientInterface from './Contracts/HttpClientInterface';
3
3
  import HttpClientResponse from './HttpClientResponse';
4
4
  import { HttpClientFailureJudgeClosure, LogHandler } from '../../Types/global';
5
+ import FormData from 'form-data';
5
6
  declare class HttpClient implements HttpClientInterface {
6
7
  protected axios: AxiosInstance;
7
8
  protected failureJudge: HttpClientFailureJudgeClosure;
@@ -19,6 +20,12 @@ declare class HttpClient implements HttpClientInterface {
19
20
  request(method: Method, url: string, payload?: AxiosRequestConfig<any>): Promise<HttpClientResponse>;
20
21
  getInstance(): AxiosInstance;
21
22
  setInstance(instance: AxiosInstance): this;
23
+ /**
24
+ * 获取 FormData 对象的 headers
25
+ * @param formData
26
+ * @returns
27
+ */
28
+ protected getFormDataHeaders(formData: FormData): Promise<Record<string, string | number>>;
22
29
  /**
23
30
  * 创建http客户端
24
31
  * @param config
@@ -11,10 +11,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
- const merge_1 = __importDefault(require("merge"));
15
14
  const axios_1 = __importDefault(require("axios"));
16
15
  const HttpClientResponse_1 = __importDefault(require("./HttpClientResponse"));
17
16
  const Utils_1 = require("../Support/Utils");
17
+ const form_data_1 = __importDefault(require("form-data"));
18
18
  class HttpClient {
19
19
  constructor(axios, failureJudge = null, throwError = false) {
20
20
  this.axios = axios;
@@ -35,7 +35,9 @@ class HttpClient {
35
35
  }
36
36
  request(method, url, payload = {}) {
37
37
  return __awaiter(this, void 0, void 0, function* () {
38
- let options = merge_1.default.recursive(true, payload);
38
+ let options = Object.assign({}, payload);
39
+ if (!options.headers)
40
+ options.headers = {};
39
41
  options.method = method;
40
42
  options.url = url;
41
43
  if (options['xml'] !== undefined) {
@@ -49,8 +51,6 @@ class HttpClient {
49
51
  else {
50
52
  throw new Error('The type of `xml` must be string or object.');
51
53
  }
52
- if (!options.headers)
53
- options.headers = {};
54
54
  if (!options.headers['Content-Type'] && !options.headers['content-type']) {
55
55
  options.headers['content-type'] = 'text/xml';
56
56
  }
@@ -69,8 +69,6 @@ class HttpClient {
69
69
  else {
70
70
  throw new Error('The type of `json` must be string or object.');
71
71
  }
72
- if (!options.headers)
73
- options.headers = {};
74
72
  if (!options.headers['Content-Type'] && !options.headers['content-type']) {
75
73
  options.headers['content-type'] = 'application/json';
76
74
  }
@@ -78,6 +76,23 @@ class HttpClient {
78
76
  options['json'] = undefined;
79
77
  delete options['json'];
80
78
  }
79
+ if (options['formData'] && Object.keys(options['formData']).length > 0) {
80
+ let formData = new form_data_1.default();
81
+ for (let key in options['formData']) {
82
+ formData.append(key, options['formData'][key]);
83
+ }
84
+ if (options.data)
85
+ for (let key in options.data) {
86
+ formData.append(key, options.data[key]);
87
+ }
88
+ options.data = formData;
89
+ options['formData'] = undefined;
90
+ delete options['formData'];
91
+ }
92
+ // 如果 data 是 FormData 对象,则从中提取 headers
93
+ if (options.data && options.data instanceof form_data_1.default) {
94
+ options.headers = Object.assign(Object.assign({}, (yield this.getFormDataHeaders(options.data))), options.headers);
95
+ }
81
96
  let starttime = Date.now();
82
97
  if (typeof this.logger === 'function') {
83
98
  yield this.logger('before', options);
@@ -97,6 +112,25 @@ class HttpClient {
97
112
  this.axios = instance;
98
113
  return this;
99
114
  }
115
+ /**
116
+ * 获取 FormData 对象的 headers
117
+ * @param formData
118
+ * @returns
119
+ */
120
+ getFormDataHeaders(formData) {
121
+ return new Promise((resolve, reject) => {
122
+ let headers = formData.getHeaders();
123
+ formData.getLength(function (err, length) {
124
+ if (err) {
125
+ headers['content-length'] = 0;
126
+ }
127
+ else {
128
+ headers['content-length'] = length;
129
+ }
130
+ resolve(headers);
131
+ });
132
+ });
133
+ }
100
134
  /**
101
135
  * 创建http客户端
102
136
  * @param config
@@ -1,4 +1,6 @@
1
+ /// <reference types="node" />
1
2
  import { AxiosRequestConfig, Method } from "axios";
3
+ import fs from "fs";
2
4
  declare class PresetMixin {
3
5
  /**
4
6
  * 存储预置参数
@@ -12,6 +14,10 @@ declare class PresetMixin {
12
14
  * 存储预置数据
13
15
  */
14
16
  protected prependData: Record<string, any>;
17
+ /**
18
+ * 存储预置文件数据
19
+ */
20
+ protected prependFiles: Record<string, fs.ReadStream>;
15
21
  /**
16
22
  * 设置预置参数
17
23
  * @param presets
@@ -68,12 +74,25 @@ declare class PresetMixin {
68
74
  * @returns
69
75
  */
70
76
  withMchIdAs(new_alias?: string): this;
77
+ /**
78
+ * 预设置文件
79
+ * @param file 文件路径或可读文件流
80
+ * @param key 参数名,默认:'file'
81
+ * @returns
82
+ */
83
+ withFile(file: string | fs.ReadStream, key?: string): this;
84
+ /**
85
+ * 预设置多个文件
86
+ * @param files 键名:文件名,键值:文件路径或可读文件流
87
+ * @returns
88
+ */
89
+ withFiles(files: Record<string, string | fs.ReadStream>): this;
71
90
  /**
72
91
  * 合并预置参数并清空预置数据
73
92
  * @param payload
74
93
  * @param method
75
94
  * @returns
76
95
  */
77
- mergeThenResetPrepends(payload: AxiosRequestConfig, method?: Method): any;
96
+ mergeThenResetPrepends(payload: AxiosRequestConfig, method?: Method): AxiosRequestConfig<any>;
78
97
  }
79
98
  export = PresetMixin;
@@ -2,7 +2,7 @@
2
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
- const merge_1 = __importDefault(require("merge"));
5
+ const fs_1 = __importDefault(require("fs"));
6
6
  class PresetMixin {
7
7
  constructor() {
8
8
  /**
@@ -17,6 +17,10 @@ class PresetMixin {
17
17
  * 存储预置数据
18
18
  */
19
19
  this.prependData = {};
20
+ /**
21
+ * 存储预置文件数据
22
+ */
23
+ this.prependFiles = {};
20
24
  }
21
25
  /**
22
26
  * 设置预置参数
@@ -123,6 +127,35 @@ class PresetMixin {
123
127
  this.with(new_alias, this.presets['mch_id']);
124
128
  return this;
125
129
  }
130
+ /**
131
+ * 预设置文件
132
+ * @param file 文件路径或可读文件流
133
+ * @param key 参数名,默认:'file'
134
+ * @returns
135
+ */
136
+ withFile(file, key = 'file') {
137
+ if (typeof this.prependFiles !== 'object') {
138
+ this.prependFiles = {};
139
+ }
140
+ if (file instanceof fs_1.default.ReadStream) {
141
+ this.prependFiles[key] = file;
142
+ }
143
+ else if (typeof file === 'string') {
144
+ this.prependFiles[key] = fs_1.default.createReadStream(file);
145
+ }
146
+ return this;
147
+ }
148
+ /**
149
+ * 预设置多个文件
150
+ * @param files 键名:文件名,键值:文件路径或可读文件流
151
+ * @returns
152
+ */
153
+ withFiles(files) {
154
+ for (const key in files) {
155
+ this.withFile(files[key], key);
156
+ }
157
+ return this;
158
+ }
126
159
  /**
127
160
  * 合并预置参数并清空预置数据
128
161
  * @param payload
@@ -132,9 +165,11 @@ class PresetMixin {
132
165
  mergeThenResetPrepends(payload, method = 'get') {
133
166
  var _a, _b, _c, _d;
134
167
  let field = method.toLowerCase() === 'get' ? 'params' : 'data';
135
- let options = merge_1.default.recursive(true, payload);
168
+ let options = Object.assign({}, payload);
136
169
  if (!options.headers)
137
170
  options.headers = {};
171
+ if (!options.formData)
172
+ options.formData = {};
138
173
  if (((_b = (_a = options.headers['Content-Type']) !== null && _a !== void 0 ? _a : options.headers['content-type']) !== null && _b !== void 0 ? _b : null) === 'application/json' || !!options.json) {
139
174
  field = 'json';
140
175
  }
@@ -147,8 +182,12 @@ class PresetMixin {
147
182
  if (this.prependHeaders && Object.keys(this.prependHeaders).length > 0) {
148
183
  options.headers = Object.assign(Object.assign({}, this.prependHeaders), options.headers);
149
184
  }
185
+ if (this.prependFiles && Object.keys(this.prependFiles).length > 0) {
186
+ options.formData = Object.assign(Object.assign({}, this.prependFiles), options.formData);
187
+ }
150
188
  this.prependData = {};
151
189
  this.prependHeaders = {};
190
+ this.prependFiles = {};
152
191
  return options;
153
192
  }
154
193
  }
@@ -49,7 +49,11 @@ class Message {
49
49
  */
50
50
  static createFromRequest(request) {
51
51
  return __awaiter(this, void 0, void 0, function* () {
52
- let originContent = request.getBody().toString();
52
+ let originContent = '';
53
+ let body = request.getBody();
54
+ if (body) {
55
+ originContent = body.toString();
56
+ }
53
57
  let attributes = {};
54
58
  if ('<' === originContent.substring(0, 1)) {
55
59
  attributes = yield (0, Utils_1.parseXml)(originContent);
@@ -75,11 +75,6 @@ class Application {
75
75
  }
76
76
  getServer() {
77
77
  if (!this.server) {
78
- let token = this.getAccount().getToken();
79
- let aesKey = this.getAccount().getAesKey();
80
- if (!token || !aesKey) {
81
- throw new Error('token or aes_key cannot be empty.');
82
- }
83
78
  this.server = new Server_1.default(this.getRequest(), this.getAccount().getAesKey() ? this.getEncryptor() : null);
84
79
  }
85
80
  return this.server;
@@ -130,7 +125,7 @@ class Application {
130
125
  }
131
126
  getTicket() {
132
127
  if (!this.ticket) {
133
- this.ticket = new JsApiTicket_1.default(this.getAccount().getAppId(), this.getAccount().getSecret(), null, this.getCache(), this.getHttpClient());
128
+ this.ticket = new JsApiTicket_1.default(this.getAccount().getAppId(), this.getAccount().getSecret(), null, this.getCache(), this.getClient());
134
129
  }
135
130
  return this.ticket;
136
131
  }
@@ -28,7 +28,7 @@ class Server extends ServerInterface_1.default {
28
28
  return __awaiter(this, void 0, void 0, function* () {
29
29
  let echostr = this.request.getQueryParams()['echostr'] || '';
30
30
  if (!!echostr) {
31
- return new Response_1.default(200, {}, echostr);
31
+ return new Response_1.default(200, { 'Content-Type': 'text/html' }, echostr);
32
32
  }
33
33
  let message = yield this.getRequestMessage(this.request);
34
34
  let query = this.request.getQueryParams();
@@ -2,6 +2,14 @@ import Application from './Application';
2
2
  declare class Utils {
3
3
  protected app: Application;
4
4
  constructor(app: Application);
5
+ /**
6
+ * 构建jssdk配置
7
+ * @param url 完整URL地址
8
+ * @param jsApiList api列表,默认:[]。可用列表:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#63
9
+ * @param openTagList 开放标签列表,默认:[]。可用列表:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_Open_Tag.html#附录-所有开放标签列表
10
+ * @param debug 是否开启调试模式,默认:false
11
+ * @returns
12
+ */
5
13
  buildJsSdkConfig(url: string, jsApiList?: string[], openTagList?: string[], debug?: boolean): Promise<Record<string, any>>;
6
14
  }
7
15
  export = Utils;
@@ -16,6 +16,14 @@ class Utils {
16
16
  constructor(app) {
17
17
  this.app = app;
18
18
  }
19
+ /**
20
+ * 构建jssdk配置
21
+ * @param url 完整URL地址
22
+ * @param jsApiList api列表,默认:[]。可用列表:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#63
23
+ * @param openTagList 开放标签列表,默认:[]。可用列表:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_Open_Tag.html#附录-所有开放标签列表
24
+ * @param debug 是否开启调试模式,默认:false
25
+ * @returns
26
+ */
19
27
  buildJsSdkConfig(url, jsApiList = [], openTagList = [], debug = false) {
20
28
  return __awaiter(this, void 0, void 0, function* () {
21
29
  return (0, merge_1.default)({
@@ -1,4 +1,4 @@
1
- import { AxiosRequestConfig } from 'axios';
1
+ import { AxiosRequestConfig, AxiosResponse } from 'axios';
2
2
  import ProviderInterface from 'node-socialite/dist/Core/ProviderInterface';
3
3
  import OfficialAccountApplicationInterface from '../OfficialAccount/Contracts/ApplicationInterface';
4
4
  import Message from '../Core/Message';
@@ -14,6 +14,10 @@ declare module 'axios' {
14
14
  * 要发送的json数据,会自动解析并赋值到data属性,同时设置content-type=application/json
15
15
  */
16
16
  json?: string | Record<string, any>;
17
+ /**
18
+ * 要发送的FormData数据,会自动解析并赋值到data属性,同时设置根据内容提取headers
19
+ */
20
+ formData?: Record<string, any>;
17
21
  }
18
22
  }
19
23
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-easywechat",
3
- "version": "3.0.0-beta.2",
3
+ "version": "3.0.0-beta.5",
4
4
  "description": "EasyWechat SDK for Node.js (NOT OFFICIAL)",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -20,7 +20,7 @@
20
20
  "license": "MIT",
21
21
  "devDependencies": {
22
22
  "@types/node": "^17.0.23",
23
- "axios-mock-adapter": "^1.20.0",
23
+ "axios-mock-adapter": "^1.21.1",
24
24
  "mocha": "^9.2.2",
25
25
  "package-release": "^1.0.2",
26
26
  "typescript": "^4.6.3"
@@ -1 +0,0 @@
1
- {"data":"mock-access_token","lifeTime":1655828727}
@@ -1 +0,0 @@
1
- {"data":"mock-access_token","lifeTime":1655828727}
@@ -1 +0,0 @@
1
- {"data":"mock-ticket","lifeTime":1655834427}