parent
							
								
									fc82fab1cf
								
							
						
					
					
						commit
						3ceb8cdc93
					
				| @ -1,16 +0,0 @@ | ||||
| .DS_Store | ||||
| node_modules/ | ||||
| dist/ | ||||
| npm-debug.log* | ||||
| yarn-debug.log* | ||||
| yarn-error.log* | ||||
| package-lock.json | ||||
| tests/**/coverage/ | ||||
| 
 | ||||
| # Editor directories and files | ||||
| .idea | ||||
| .vscode | ||||
| *.suo | ||||
| *.ntvs* | ||||
| *.njsproj | ||||
| *.sln | ||||
| @ -1,213 +0,0 @@ | ||||
| const axios = require('axios'); | ||||
| const moment = require('moment'); | ||||
| const qs = require('qs'); | ||||
| 
 | ||||
| const RequestType = require('./RequestType'); | ||||
| const SignUtil = require('./SignUtil'); | ||||
| const BaseRequest = require('./BaseRequest'); | ||||
| 
 | ||||
| const IS_RUN_IN_BROWSER = typeof window !== 'undefined' && this === window; | ||||
| 
 | ||||
| const HEADERS = {'Accept-Encoding': 'identity'}; | ||||
| 
 | ||||
| const getHeaders = (headers = {}) => { | ||||
|     return Object.assign({}, headers, HEADERS); | ||||
| }; | ||||
| 
 | ||||
| const parseResponse = (error, response, request) => { | ||||
|     if (!error && response.status === 200) { | ||||
|         return request.parseResponse(response.data); | ||||
|     } else { | ||||
|         return { // 重新封装请求异常回调,以防中断
 | ||||
|             msg: '请求异常', | ||||
|             code: '502', | ||||
|             sub_msg: `${error}`, | ||||
|             sub_code: 'isv.invalid-server' | ||||
|         }; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| const buildParams = (instance, request, token) => { | ||||
|     const {appId, privateKey} = instance; | ||||
|     const allParams = { | ||||
|         'app_id': appId, | ||||
|         'method': request.getMethod(), | ||||
|         'charset': 'UTF-8', | ||||
|         'sign_type': 'RSA2', | ||||
|         'timestamp': moment().format('YYYY-MM-DD HH:mm:ss'), | ||||
|         'version': request.getVersion(), | ||||
|         'biz_content': JSON.stringify(request.bizModel) | ||||
|     }; | ||||
| 
 | ||||
|     if (token) { | ||||
|         allParams['app_auth_token'] = token; | ||||
|     } | ||||
|     // 创建签名
 | ||||
|     allParams.sign = SignUtil.createSign(allParams, privateKey, 'RSA2'); | ||||
|     return allParams; | ||||
| }; | ||||
| 
 | ||||
| const executeRequest = async (instance = {}, request, token, callback, customOptions = {}) => { | ||||
|     const params = buildParams(instance, request, token); | ||||
|     const {url} = instance; | ||||
|     let {headers} = customOptions; | ||||
|     const config = { | ||||
|         url, | ||||
|         method: 'POST', | ||||
|         params: undefined, | ||||
|         data: undefined | ||||
|     }; | ||||
|     headers = getHeaders(headers); | ||||
|     const requestType = request.getRealRequestType(); | ||||
|     switch (requestType) { | ||||
|         case RequestType.GET: { | ||||
|             config.method = 'GET'; | ||||
|             config.params = params; | ||||
|         } | ||||
|             break; | ||||
|         case RequestType.POST_FORM: { | ||||
|             headers = Object.assign(headers, { | ||||
|                 'Content-Type': 'application/x-www-form-urlencoded' | ||||
|             }); | ||||
|             config.data = qs.stringify(params); | ||||
|         } | ||||
|             break; | ||||
|         case RequestType.POST_JSON: { | ||||
|             config.data = params; | ||||
|         } | ||||
|             break; | ||||
|         case RequestType.POST_FILE: { | ||||
|             let formData; | ||||
|             if (IS_RUN_IN_BROWSER) { | ||||
|                 formData = new window.FormData() | ||||
|                 (request.files || []).forEach(({name, path}) => { | ||||
|                     formData.append(name, path, { | ||||
|                         contentType: 'application/octet-stream' | ||||
|                     }); | ||||
|                 }); | ||||
|             } else { | ||||
|                 const fs = require('fs'); | ||||
|                 const fd = require('form-data'); | ||||
|                 formData = new fd(); | ||||
|                 (request.files || []).forEach(({name, path}) => { | ||||
|                     formData.append(name, fs.createReadStream(path), { | ||||
|                         contentType: 'application/octet-stream' | ||||
|                     }); | ||||
|                 }); | ||||
|                 headers = Object.assign(headers, formData.getHeaders()); | ||||
|             } | ||||
|             Object.keys(params).forEach(key => { | ||||
|                 const value = params[key]; | ||||
|                 if (!(typeof key === 'undefined' || typeof value === 'undefined')) { | ||||
|                     formData.append(key, params[key]); | ||||
|                 } | ||||
|             }); | ||||
|             config.data = formData; | ||||
|         } | ||||
|             break; | ||||
|         default: { | ||||
|             callback(parseResponse(new Error('request.getRequestType()类型不正确'), undefined, request)); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     try { | ||||
|         config['headers'] = headers; | ||||
|         const response = await axios.request(config); | ||||
|         callback(parseResponse(undefined, response, request)); | ||||
|     } catch (error) { | ||||
|         callback(parseResponse(error, undefined, request)); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| module.exports = class OpenClient { | ||||
|     /** | ||||
|      * 初始化客户端 | ||||
|      * @param appId 应用ID | ||||
|      * @param privateKey 应用私钥,2048位,PKCS8 | ||||
|      * @param url 请求url | ||||
|      */ | ||||
|     constructor(appId, privateKey, url) { | ||||
|         this.appId = appId || ''; | ||||
|         this.privateKey = privateKey || ''; | ||||
|         this.url = url || ''; | ||||
|     } | ||||
| 
 | ||||
|     setAppId(appId) { | ||||
|         this.appId = appId || ''; | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     setPrivateKey(privateKey) { | ||||
|         this.privateKey = privateKey || ''; | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     setUrl(url) { | ||||
|         this.url = url || ''; | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 发送请求 | ||||
|      * @param request 请求类 | ||||
|      * @param callback 回调函数,参数json(undefined则使用executeSync) | ||||
|      * @param options 自定义参数,如headers | ||||
|      */ | ||||
|     execute(request, callback, options) { | ||||
|         if (typeof callback == 'function') { | ||||
|             return this.executeToken(request, null, callback, options); | ||||
|         } else { | ||||
|             return this.executeSync(request, options); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 发送同步请求 | ||||
|      * @param request 请求类 | ||||
|      * @param options 自定义参数,如headers | ||||
|      * */ | ||||
|     executeSync(request, options) { | ||||
|         return new Promise((resolve) => { | ||||
|             const _ = this.execute(request, res => { | ||||
|                 resolve(res); | ||||
|             }, options); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 发送请求 | ||||
|      * @param request 请求类 | ||||
|      * @param token token | ||||
|      * @param callback 回调函数,参数json(undefined则使用executeTokenSync) | ||||
|      * @param options 自定义参数,如headers | ||||
|      */ | ||||
|     async executeToken(request, token, callback, options) { | ||||
|         if (!(request instanceof BaseRequest)) { | ||||
|             throw 'request类未继承BaseRequest'; | ||||
|         } | ||||
|         if (typeof callback == 'function') { | ||||
|             const files = request.files; | ||||
|             if (files && files.length > 0) { | ||||
|                 request.setForceRequestType(RequestType.POST_FILE); | ||||
|             } | ||||
|             return await executeRequest(this, request, token, callback, options); | ||||
|         } else { | ||||
|             return this.executeTokenSync(request, token); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 发送同步请求 | ||||
|      * @param request 请求类 | ||||
|      * @param token token | ||||
|      * @param options 自定义参数,如headers | ||||
|      */ | ||||
|     executeTokenSync(request, token, options) { | ||||
|         return new Promise((resolve) => { | ||||
|             const _ = this.executeToken(request, token, res => { | ||||
|                 resolve(res); | ||||
|             }, options); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| }; | ||||
| @ -1,6 +0,0 @@ | ||||
| module.exports = { | ||||
|     GET: 'GET', | ||||
|     POST_FORM: 'POST_FORM', | ||||
|     POST_JSON: 'POST_JSON', | ||||
|     POST_FILE: 'POST_FILE' | ||||
| }; | ||||
| @ -1,88 +0,0 @@ | ||||
| const {KJUR, hextob64} = require('jsrsasign'); | ||||
| 
 | ||||
| const HashMap = { | ||||
|     SHA256withRSA: 'SHA256withRSA', | ||||
|     SHA1withRSA: 'SHA1withRSA' | ||||
| }; | ||||
| 
 | ||||
| const PEM_BEGIN = '-----BEGIN PRIVATE KEY-----\n'; | ||||
| const PEM_END = '\n-----END PRIVATE KEY-----'; | ||||
| 
 | ||||
| /** | ||||
|  * rsa签名参考:https://www.jianshu.com/p/145eab95322c
 | ||||
|  */ | ||||
| module.exports = { | ||||
|     /** | ||||
|      * 创建签名 | ||||
|      * @param params 请求参数 | ||||
|      * @param privateKey 私钥,PKCS8 | ||||
|      * @param signType 签名类型,RSA,RSA2 | ||||
|      * @returns 返回签名内容 | ||||
|      */ | ||||
|     createSign(params, privateKey, signType) { | ||||
|         const content = this.getSignContent(params); | ||||
|         return this.sign(content, privateKey, signType); | ||||
|     }, | ||||
|     sign: function (content, privateKey, signType) { | ||||
|         if (signType.toUpperCase() === 'RSA') { | ||||
|             return this.rsaSign(content, privateKey, HashMap.SHA1withRSA); | ||||
|         } else if (signType.toUpperCase() === 'RSA2') { | ||||
|             return this.rsaSign(content, privateKey, HashMap.SHA256withRSA); | ||||
|         } else { | ||||
|             throw 'signType错误'; | ||||
|         } | ||||
|     }, | ||||
|     /** | ||||
|      * rsa签名 | ||||
|      * @param content 签名内容 | ||||
|      * @param privateKey 私钥 | ||||
|      * @param hash hash算法,SHA256withRSA,SHA1withRSA | ||||
|      * @returns 返回签名字符串,base64 | ||||
|      */ | ||||
|     rsaSign: function (content, privateKey, hash) { | ||||
|         privateKey = this._formatKey(privateKey); | ||||
|         // 创建 Signature 对象
 | ||||
|         const signature = new KJUR.crypto.Signature({ | ||||
|             alg: hash, | ||||
|             //!这里指定 私钥 pem!
 | ||||
|             prvkeypem: privateKey | ||||
|         }); | ||||
|         signature.updateString(content); | ||||
|         const signData = signature.sign(); | ||||
|         // 将内容转成base64
 | ||||
|         return hextob64(signData); | ||||
|     }, | ||||
|     _formatKey: function (key) { | ||||
|         if (!key.startsWith(PEM_BEGIN)) { | ||||
|             key = PEM_BEGIN + key; | ||||
|         } | ||||
|         if (!key.endsWith(PEM_END)) { | ||||
|             key = key + PEM_END; | ||||
|         } | ||||
|         return key; | ||||
|     }, | ||||
|     /** | ||||
|      * 获取签名内容 | ||||
|      * @param params 请求参数 | ||||
|      * @returns {string} | ||||
|      */ | ||||
|     getSignContent: function (params) { | ||||
|         const paramNames = []; | ||||
|         for (const key in params) { | ||||
|             paramNames.push(key); | ||||
|         } | ||||
| 
 | ||||
|         paramNames.sort(); | ||||
| 
 | ||||
|         const paramNameValue = []; | ||||
| 
 | ||||
|         for (let i = 0, len = paramNames.length; i < len; i++) { | ||||
|             const paramName = paramNames[i]; | ||||
|             const val = params[paramName]; | ||||
|             if (paramName && val) { | ||||
|                 paramNameValue.push(`${paramName}=${val}`); | ||||
|             } | ||||
|         } | ||||
|         return paramNameValue.join('&'); | ||||
|     } | ||||
| }; | ||||
| @ -1,44 +0,0 @@ | ||||
| const OpenClient = require('./common/OpenClient'); | ||||
| 
 | ||||
| const StoryGetRequest = require('./request/StoryGetRequest'); | ||||
| 
 | ||||
| // 应用ID
 | ||||
| const appId = '2019032617262200001'; | ||||
| // 应用私钥,2048位,PKCS8
 | ||||
| const privateKey = 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ='; | ||||
| // 接口url
 | ||||
| const url = 'http://localhost:8081'; | ||||
| 
 | ||||
| // 创建客户端
 | ||||
| const openClient = new OpenClient().setUrl(url).setAppId(appId).setPrivateKey(privateKey); | ||||
| 
 | ||||
| (async () => { | ||||
|     // 创建请求
 | ||||
|     const request = new StoryGetRequest(); | ||||
| 
 | ||||
|     // 设置业务参数
 | ||||
|     const bizModel = { | ||||
|         id: 111, | ||||
|         name: 'jim' | ||||
|     }; | ||||
|     request.setBizModel(bizModel); | ||||
| 
 | ||||
|     // 添加上传文件
 | ||||
|     // 批量添加
 | ||||
|     // const files = [
 | ||||
|     //     // name: 表单名称,path:文件全路径
 | ||||
|     //     {name: 'file1', path: `${__dirname}/main.js`},
 | ||||
|     //     {name: 'file2', path: `${__dirname}/readme.md`}
 | ||||
|     // ];
 | ||||
|     // request.setFiles(files);
 | ||||
|     // // 单个添加
 | ||||
|     // request.addFile('file3', `${__dirname}/package.json`);
 | ||||
| 
 | ||||
|     const data = await openClient.executeSync(request); | ||||
|     // 成功
 | ||||
|     if (!data.sub_code) { | ||||
|         console.log('成功', data); | ||||
|     } else { | ||||
|         console.error('失败', data); | ||||
|     } | ||||
| })(); | ||||
| @ -1,21 +0,0 @@ | ||||
| { | ||||
|   "name": "sdk-nodejs", | ||||
|   "version": "1.0.0", | ||||
|   "description": "", | ||||
|   "main": "index.js", | ||||
|   "scripts": { | ||||
|     "test": "echo \"Error: no test specified\" && exit 1" | ||||
|   }, | ||||
|   "keywords": [], | ||||
|   "author": "", | ||||
|   "license": "ISC", | ||||
|   "dependencies": { | ||||
|     "axios": "^0.21.1", | ||||
|     "form-data": "^4.0.0", | ||||
|     "isarray": "^2.0.5", | ||||
|     "isobject": "^4.0.0", | ||||
|     "jsrsasign": "^8.0.19", | ||||
|     "moment": "^2.27.0", | ||||
|     "qs": "^6.10.1" | ||||
|   } | ||||
| } | ||||
| @ -1,10 +0,0 @@ | ||||
| - 执行`npm install --registry=https://registry.npm.taobao.org` | ||||
| 
 | ||||
| - 执行`node main.js`进行测试 | ||||
| 
 | ||||
| ## 封装步骤 | ||||
| 
 | ||||
| 1. 新建request类,继承BaseRequest,参考`StoryGetRequest.js` | ||||
| 
 | ||||
| 2. 调用接口,参考`main.js` | ||||
| 
 | ||||
| @ -1,19 +0,0 @@ | ||||
| const BaseRequest = require('../common/BaseRequest'); | ||||
| const RequestType = require('../common/RequestType'); | ||||
| 
 | ||||
| /** | ||||
|  * 创建一个请求类,继承BaseRequest,重写三个函数 | ||||
|  */ | ||||
| module.exports = class StoryGetRequest extends BaseRequest { | ||||
|     getMethod() { | ||||
|         return 'story.get'; | ||||
|     } | ||||
| 
 | ||||
|     getVersion() { | ||||
|         return '1.0'; | ||||
|     } | ||||
| 
 | ||||
|     getRequestType() { | ||||
|         return RequestType.GET; | ||||
|     } | ||||
| }; | ||||
					Loading…
					
					
				
		Reference in new issue