{ "info": { "_postman_id": "baecfa82-e306-40b0-a149-6c8d0321f511", "name": "openapi-call-example", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "item": [ { "name": "https://open.byteplusapi.com/?Action=ListActivityAPI&Version=2020-06-01", "event": [ { "listen": "prerequest", "script": { "exec": [ "const crypto = require('crypto-js');", "const { SHA256, HmacSHA256: hmacSHA256, enc } = crypto;", "const util = {", " crypto: {", " hmac: function hmac(key, string) {", " return hmacSHA256(string, key);", " },", "", " sha256: function sha256(data) {", " return SHA256(data);", " },", " },", "};", "", "/**", " * @api private", " */", "const expiresHeader = 'presigned-expires';", "", "const unsignableHeaders = [", " 'authorization',", " 'content-type',", " 'content-length',", " 'user-agent',", " expiresHeader,", " 'expect',", " 'x-amzn-trace-id',", "];", "", "const uriEscape = (str) => {", " try {", " return encodeURIComponent(str)", " .replace(/[^A-Za-z0-9_.~\\-%]+/g, escape)", " .replace(", " /[*]/g,", " (ch) => `%${ch.charCodeAt(0).toString(16).toUpperCase()}`", " );", " } catch (e) {", " return '';", " }", "};", "", "const queryParamsToString = (params, sort = true) =>", " (sort ? Object.keys(params).sort() : Object.keys(params))", " .map((key) => {", " const val = params[key];", " if (typeof val === 'undefined' || val === null) {", " return;", " }", "", " const escapedKey = uriEscape(key);", " if (!escapedKey) {", " return;", " }", "", " if (Array.isArray(val)) {", " return `${escapedKey}=${val", " .map(uriEscape)", " .sort()", " .join(`&${escapedKey}=`)}`;", " }", "", " return `${escapedKey}=${uriEscape(val)}`;", " })", " .filter((v) => v)", " .join('&');", "", "/**", " * @api private", " */", "class AWSSignersV4 {", " constructor(request, serviceName, options) {", " this.request = request;", " this.request.headers = request.headers || {};", " this.serviceName = serviceName;", " options = options || {};", " this.signatureCache =", " typeof options.signatureCache === 'boolean'", " ? options.signatureCache", " : true;", " this.operation = options.operation;", " this.signatureVersion = options.signatureVersion;", " this.constant = options.isVolcengine", " ? {", " algorithm: 'HMAC-SHA256',", " v4Identifier: 'request',", " dateHeader: 'X-Date',", " tokenHeader: 'x-security-token',", " contentSha256Header: 'X-Content-Sha256',", " kDatePrefix: '',", " }", " : {", " algorithm: 'AWS4-HMAC-SHA256',", " v4Identifier: 'aws4_request',", " dateHeader: 'X-Amz-Date',", " tokenHeader: 'x-amz-security-token',", " contentSha256Header: 'X-Amz-Content-Sha256',", " kDatePrefix: 'AWS4',", " };", " this.bodySha256 = options.bodySha256;", " }", "", " addAuthorization(credentials, date) {", " const datetime = this.iso8601(date).replace(/[:\\-]|\\.\\d{3}/g, '');", " this.addHeaders(credentials, datetime);", " this.request.headers['Authorization'] = this.authorization(", " credentials,", " datetime", " );", " }", "", " addHeaders(credentials, datetime) {", " this.request.headers[this.constant.dateHeader] = datetime;", " if (credentials.sessionToken) {", " this.request.headers[this.constant.tokenHeader] =", " credentials.sessionToken;", " }", " if (this.request.body) {", " let body = this.request.body;", " if (typeof body !== 'string') {", " if (body instanceof URLSearchParams) {", " body = body.toString();", " } else {", " body = JSON.stringify(body);", " }", " }", " this.request.headers[this.constant.contentSha256Header] =", " this.bodySha256 || util.crypto.sha256(body).toString();", " }", " }", "", " authorization(credentials, datetime) {", " const parts = [];", " const credString = this.credentialString(datetime);", " parts.push(", " `${this.constant.algorithm} Credential=${credentials.accessKeyId}/${credString}`", " );", " parts.push(`SignedHeaders=${this.signedHeaders()}`);", " const signature = this.signature(credentials, datetime);", " console.log('signature is', signature.toString(enc.Hex));", " parts.push(`Signature=${signature}`);", " return parts.join(', ');", " }", "", " signature(credentials, datetime) {", " const signingKey = this.getSigningKey(", " credentials,", " datetime.substr(0, 8),", " this.request.region,", " this.serviceName,", " this.signatureCache", " );", " const signStr = this.stringToSign(datetime);", " console.log('signStr is', signStr);", " console.log('signingKey is', signingKey.toString(enc.Hex));", " return util.crypto.hmac(signingKey, signStr, 'hex');", " }", "", " stringToSign(datetime) {", " const parts = [];", " parts.push(this.constant.algorithm);", " parts.push(datetime);", " parts.push(this.credentialString(datetime));", " const canonicalString = this.canonicalString();", " console.log('canonicalString is', canonicalString);", " parts.push(this.hexEncodedHash(canonicalString));", "", " return parts.join('\\n');", " }", "", " canonicalString() {", " const parts = [],", " pathname = this.request.pathname || '/';", "", " parts.push(this.request.method.toUpperCase());", " parts.push(pathname);", " parts.push(queryParamsToString(this.request.params) || '');", " parts.push(`${this.canonicalHeaders()}\\n`);", " parts.push(this.signedHeaders());", " parts.push(this.hexEncodedBodyHash());", " return parts.join('\\n');", " }", "", " canonicalHeaders() {", " const headers = [];", " Object.keys(this.request.headers).forEach((key) => {", " headers.push([key, this.request.headers[key]]);", " });", " headers.sort((a, b) => (a[0].toLowerCase() < b[0].toLowerCase() ? -1 : 1));", " const parts = [];", " headers.forEach((item) => {", " const key = item[0].toLowerCase();", " if (this.isSignableHeader(key)) {", " const value = item[1];", " if (", " typeof value === 'undefined' ||", " value === null ||", " typeof value.toString !== 'function'", " ) {", " throw new Error(`Header ${key} contains invalid value`);", " }", " parts.push(`${key}:${this.canonicalHeaderValues(value.toString())}`);", " }", " });", " return parts.join('\\n');", " }", "", " canonicalHeaderValues(values) {", " return values.replace(/\\s+/g, ' ').replace(/^\\s+|\\s+$/g, '');", " }", "", " signedHeaders() {", " const keys = [];", " Object.keys(this.request.headers).forEach((key) => {", " key = key.toLowerCase();", " if (this.isSignableHeader(key)) {", " keys.push(key);", " }", " });", " return keys.sort().join(';');", " }", "", " credentialString(datetime) {", " return this.createScope(", " datetime.substr(0, 8),", " this.request.region,", " this.serviceName", " );", " }", "", " hexEncodedHash(string) {", " return util.crypto.sha256(string);", " }", "", " hexEncodedBodyHash() {", " if (this.request.headers[this.constant.contentSha256Header]) {", " return this.request.headers[this.constant.contentSha256Header];", " }", "", " if (this.request.body) {", " return this.hexEncodedHash(queryParamsToString(this.request.body));", " }", " return this.hexEncodedHash('');", " }", "", " isSignableHeader(key) {", " if (key.toLowerCase().indexOf('x-amz-') === 0) {", " return true;", " }", " return unsignableHeaders.indexOf(key) < 0;", " }", "", " iso8601(date) {", " if (date === undefined) {", " date = new Date();", " }", " return date.toISOString().replace(/\\.\\d{3}Z$/, 'Z');", " }", "", " getSigningKey(credentials, date, region, service) {", " // const credsIdentifier = crypto.hmac(credentials.secretAccessKey, credentials.accessKeyId, 'base64');", " // const cacheKey = [credsIdentifier, date, region, service].join('_');", "", " const kDate = util.crypto.hmac(", " `${this.constant.kDatePrefix}${credentials.secretAccessKey}`,", " date", " );", " // debugger;", " const kRegion = util.crypto.hmac(kDate, region);", " const kService = util.crypto.hmac(kRegion, service);", "", " const signingKey = util.crypto.hmac(kService, this.constant.v4Identifier);", "", " return signingKey;", " }", "", " createScope(date, region, serviceName) {", " return [", " date.substr(0, 8),", " region,", " serviceName,", " this.constant.v4Identifier,", " ].join('/');", " }", "}", "", "/**", " * 获取值 支持变量语法", " * @param {string} value ", " * @returns ", " */", "function getValue(value = '') {", " const variableKeyMatch = value.match(/^\\{\\{(.+?)\\}\\}$/);", " const variableKey = variableKeyMatch && variableKeyMatch[1];", " if (variableKey) {", " return pm.environment.get(variableKey) || value;", " } else {", " return value;", " }", "}", "", "/**", " * 将postman的kv list转换为map 并过滤disabled参数", " * @param {*} list ", " * @returns ", " */", "function getMap(list) {", " const map = {};", " list.forEach((item) => {", " if (!item.disabled) {", " const value = typeof item.value === 'string' ? getValue(item.value) : '';", " // 重复query处理", " if (map[item.key]) {", " map[item.key] = Array.isArray(map[item.key])", " ? [...map[item.key], value]", " : [map[item.key], value];", " } else map[item.key] = value;", " }", " });", " return map;", "}", "// 获取转换后的headers和params", "const headers = getMap(pm.request.headers.all());", "const params = getMap(pm.request.url.query.all());", "", "// 从headers中解析出签名所需参数", "const {", " AccessKey,", " SecretKey,", " ServiceName,", " Region = 'cn-north-1',", " isVolcEngine = 'true',", " 'X-Security-Token': sessionToken,", "} = headers;", "", "// 经过处理的header 不再透传", "const handleKeys = [", " 'AccessKey',", " 'SecretKey',", " 'ServiceName',", " 'Region',", " 'isVolcEngine',", " 'X-Security-Token',", "];", "handleKeys.forEach((key) => {", " delete headers[key];", " pm.request.headers.remove(key);", "});", "", "// 构造签名对象", "const signObj = {", " region: Region,", " headers: {}, // 默认不签名用户传递的headers 防止tlb改header导致的签名错误 本代码用于开发自测 这部分安全性可以忽略", " params,", " method: pm.request.method,", " pathname: '/' + (pm.request.url.path ? pm.request.url.path.join('/') : ''),", "};", "", "// 串化各种情况下的body", "if (pm.request.body) {", " switch (pm.request.body.mode) {", " case 'raw':", " signObj.body = pm.request.body.raw;", " break;", " case 'urlencoded':", " const bodys = getMap(pm.request.body.urlencoded.all());", " signObj.body = queryParamsToString(bodys, false);", " break;", " case 'formdata': {", " const enableBodys = pm.request.body.formdata.filter(", " (item) => !item.disabled", " );", " if (enableBodys.length === 0) {", " break;", " }", " // 自己生成boundary", " const boundary = `--------------------------${Date.now()}`;", " pm.request.headers.add({", " key: 'Content-Type',", " value: `multipart/form-data; boundary=${boundary}`,", " });", " signObj.body = `--${boundary}\\r\\n${enableBodys", " .map(", " (item) =>", " `Content-Disposition: form-data; name=\"${", " item.key", " }\"\\r\\n\\r\\n${getValue(item.value)}\\r\\n`", " )", " .join(`--${boundary}\\r\\n`)}--${boundary}--\\r\\n`;", " break;", " }", " default:", " break;", " }", "}", "", "// 执行签名", "const signer = new AWSSignersV4(signObj, ServiceName, {", " isVolcengine: isVolcEngine !== 'false',", "});", "signer.addAuthorization({", " accessKeyId: AccessKey,", " secretAccessKey: SecretKey,", " sessionToken,", "});", "", "// 将签名后的headers注入到请求的headers中", "Object.keys(signer.request.headers).forEach((key) => {", " pm.request.headers.add({", " key,", " value: signer.request.headers[key],", " });", "});", "", "// 对query做encode处理 同时通过queryParamsToString保证签名时的querystring与发送出请求的一致", "if (pm.request.url.query) {", " pm.request.url.query = queryParamsToString(params);", "}", "", "", "// 构造curl命令 用于自行debug", "try {", " function getCurlBodyString(body) {", " if (!body) return '';", " switch (body.mode) {", " case 'raw':", " return `--header 'Content-Type: application/json' \\\\", "--data-raw '${body.raw}'`;", " case 'urlencoded':", " const bodys = getMap(body.urlencoded.all());", " const bodyString = queryParamsToString(bodys, false);", " return `--header 'Content-Type: application/x-www-form-urlencoded' \\\\", "${bodyString", " .split('&')", " .map((v) => `--data-urlencode '${v}'`)", " .join(' \\\\\\n')}`;", " case 'formdata': {", " // formdata怎么curl 还没想明白", " throw new Error('can not generate curl of formdata');", " }", " default:", " break;", " }", " }", " const headerString = pm.request.headers", " .toString()", " .split('\\n')", " .map((line) => `--header '${line}'`)", " .join(' \\\\\\n');", " const bodyString = getCurlBodyString(pm.request.body);", " const url = `${pm.request.url.protocol}://${pm.request.url.host.join('.')}${", " pm.request.path ? pm.request.path.join('/') : '/'", " }?${pm.request.url.query}`;", " // 打印出curl请求", " console.log(", " [", " `curl --location --request ${pm.request.method} '${url}'`,", " headerString,", " bodyString,", " ]", " .filter((v) => !!v)", " .join(' \\\\\\n')", " );", "} catch (e) {", " console.log(e);", "}", "" ], "type": "text/javascript" } } ], "request": { "auth": { "type": "noauth" }, "method": "POST", "header": [ { "key": "AccessKey", "value": "YOUR AK", "type": "text" }, { "key": "SecretKey", "value": "YOUR SK", "type": "text" }, { "key": "Region", "value": "ap-singapore-1", "type": "text" }, { "key": "ServiceName", "value": "livesaas", "type": "text" } ], "body": { "mode": "raw", "raw": "{\n \"PageNo\": 1,\n \"PageItemCount\": 100\n}", "options": { "raw": { "language": "json" } } }, "url": { "raw": "https://open.byteplusapi.com/?Action=ListActivityAPI&Version=2020-06-01", "protocol": "https", "host": [ "open", "byteplusapi", "com" ], "path": [ "" ], "query": [ { "key": "Action", "value": "ListActivityAPI" }, { "key": "Version", "value": "2020-06-01" } ] } }, "response": [] } ] }