全部产品
存储与CDN 数据库 安全 应用服务 数加·人工智能 数加·大数据基础服务 互联网中间件 视频服务 开发者工具 解决方案 物联网
推荐引擎

API说明 V2.0

更新时间:2017-06-20 16:36:45

API V2.0

API V2.0接口适用于推荐引擎旧版控制台,如果使用新版控制台,请参考API说明 V3.0

参数说明

host:shujuapi.aliyun.com

dplus_org_code:用户数加申请到的org_code

注意:使用启动离线算法任务 启动数据预处理任务 启动效果计算任务 这三个API时, 需要增加 header:x-dataplus-timeout=60000

* 请参见数加鉴权规范

1、获取推荐结果

接口说明:获取在线推荐结果

API用户授权类型:数加API鉴权

请参见 数加鉴权规范

地址:${host}/${dplus_org_code}/re/doRec

method: GET

query参数:

参数 类型 是否必填 实例 默认值 描述
biz_code string “biz” null 业务code
scn_code string “scn” null 场景code
user_id string “u1” null 用户id
item_id string “u1” null 物品id

输入样例(不包括数加身份验证headers,下同):

  1. curl -X GET "${host}/${dplus_org_code}/re/doRec?biz_code=b1&scn_code=s1&user_id=123"

返回结果:

参数 类型 实例 描述
trace_id string “277f255555a1e4ff124bdacc528b815d” 系统自动分配的任务id
rec JSONArray {} 推荐列表,已排序
abtag string “p1” ABtest标签,即path_code

返回示例:

  1. {
  2. "code":"SUCCESS",
  3. "data":{
  4. "trace_id":"t1",
  5. "rec":[["i1","0.9"],["i2","0.8"]],
  6. "abtag":"p1"
  7. }
  8. "message":null
  9. }

2、启动数据预处理任务

接口说明:启动数据预处理任务,与产品控制台“我的推荐”页面中的“启动数据预处理”功能一样

API用户授权类型: 数加API鉴权

请参见 数加鉴权规范

地址:${host}/${dplus_org_code}/re/etl

method: POST

body参数:

参数 类型 是否必填 实例 默认值 描述
biz_code string “biz” null 业务code
ds string “20150101” null 业务时间,格式yyyymmdd

输入样例:

  1. curl -X POST -d "{'biz_code':'b1','ds':'20160101'}" "${host}/${dplus_org_code}/re/etl"

返回结果:

参数 类型 实例 描述
task_id number 123 系统自动分配,用于查询任务状态

返回示例:

  1. {
  2. "code":"SUCCESS",
  3. "data":{
  4. "task_id":123
  5. }
  6. "message":null
  7. }

3、启动离线算法任务

接口说明:启动biz(业务)或者scn(场景)下面所有”线上生产环境“中已配置的算法流程。当存在多个已配置的算法流程时,每个算法流程启动的离线算法任务对应一个task_id,返回结果中的task_ids中会包含多个task_id。

API用户授权类型: 数加API鉴权

请参见 数加鉴权规范

地址:${host}/${dplus_org_code}/re/tasks

method: POST

body参数:

参数 类型 是否必填 实例 默认值 描述
biz_code string “biz” null 业务code
scn_code string “scn” null 场景code
ds string “20150101” null 业务时间,格式yyyymmdd

输入样例:

  1. curl -X POST -d "{'biz_code':'b1','ds':'20160101'}" "${host}/${dplus_org_code}/re/tasks"

返回结果:

参数 类型 实例 描述
task_ids object [1,3,4] 返回task_id 列表

返回示例:

  1. {
  2. "code":"SUCCESS",
  3. "data":{
  4. "task_ids":[1,2,3]
  5. }
  6. "message":null
  7. }

错误码:

  1. HTTP标准错误码

4、启动效果计算任务

接口说明:启动biz(业务)下的效果计算

API用户授权类型: 数加API鉴权

请参见 数加鉴权规范

地址:${host}/${dplus_org_code}/re/index

method: POST

body参数:

参数 类型 是否必填 实例 默认值 描述
biz_code string “biz” null 业务code
ds string “20150101” null 业务时间,格式yyyymmdd

输入样例:

  1. curl -X POST -d "{'biz_code':'b1','ds':'20160101'}" "${host}/${dplus_org_code}/re/index"

返回结果:

参数 类型 实例 描述
task_id number 123 系统自动分配,用于查询任务状态

返回示例:

  1. {
  2. "code":"SUCCESS",
  3. "data":{
  4. "task_id":1
  5. }
  6. "message":null
  7. }

错误码:

  1. HTTP标准错误码

5、查询任务状态

接口说明:查询离线任务的运行状态

API用户授权类型:数加API鉴权

请参见 数加鉴权规范

地址:${host}/${dplus_org_code}/re/status

method: GET

query参数:

参数 类型 是否必填 实例 默认值 描述
task_id string “123” null 作业id,传入这个参数则根据task_id 获取状态
biz_code string “biz” null 业务code
ds string “20150101” null 业务时间,格式yyyymmdd
scn_code string “scn” null 场景code
task_type int 1 null 作业类型 0:线上生产算法流程 1: 数据预处理 2:效果流程 3:测试算法流程

输入样例:

根据task_id 获取

  1. curl -X GET "${host}/${dplus_org_code}/re/status?task_id=123"

返回结果:

参数 类型 实例 描述
task_status string “SUCCESS” 三种状态 “SUCCESS”:成功,“FAILURE”:失败,“RUNNING”:正在运行

返回示例:

  1. {
  2. "code":"SUCCESS",
  3. "data":{
  4. "task_status":"RUNNING"
  5. }
  6. "message":null
  7. }

根据biz_code 查询

需要传入 biz_code 和 ds, 选填 scn_code 和 task_type 做条件筛选。

  1. curl -X GET "${host}/${dplus_org_code}/re/status?biz_code=xxx&ds=20160315"

返回结果:

参数 类型 实例 描述
task_status array [{task_id:1,task_status:”SUCCESS”}] 三种状态 “SUCCESS”:成功,“FAILURE”:失败,“RUNNING”:正在运行

返回示例:

  1. {
  2. "code":"SUCCESS",
  3. "data":[{task_id:1,task_status:"SUCCESS"}]
  4. }
  5. "message":null
  6. }

6、数据更新

接口说明:更新线上在线存储里的资源,实时生效,适用于因实时数据更新导致推荐结果变更的情况。本接口非数据上传接口,上传数据请使用“实时行为日志上传API”。

API用户授权类型:数加API鉴权

请参见 数加鉴权规范

地址:${host}/${dplus_org_code}/re/doModify

method: POST

query参数:

参数 类型 是否必填 实例 默认值 描述
target string 取值范围为[‘RecItemInfo’, ‘TagBaseRec’, ‘DefaultRec’, ‘Others’] 无默认值 资源对象标识
RecItemInfo:表明更新的资源对象为推荐池
TagBaseRec:表明更新的资源对象为您自定义的以tag(任意类型,比如城市,性别、年龄等都可以作为tag,需要您保证tag的唯一性)为推荐对象的推荐列表
DefaultRec:表明更新的资源对象为无个性化的默认推荐结果
method string 取值范围为[‘UPDATE’, ‘NEW’, ‘DELETE’],严格要求大写 无默认值 资源对象操作类型
UPDATE:更新一个已有资源
NEW:新增一个资源
DELETE:删除一个资源
biz_code string ‘mybiz’ 无默认值 业务code,表明更新该业务下的资源。由于推荐结果的产生是以算法流程为单位(最小粒度),所以如未同时指定scn_code和path_code,API将默认将更新注册在该业务下的所有算法流程资源
scn_code string ‘myscn’ 无默认值 场景code,如指定该选项,表明只更新biz_code下该场景下注册的所有算法流程资源
path_code string ‘mypath’ 无默认值 算法流程code,如指定该选项,表明只更新biz_code下的scn_code场景下注册的指定算法流程资源

输入样例:

推荐池中新增一个可推荐物品

  1. 要求post内容为json格式,结构为
  2. {
  3. item_id: xxx, // 必须,要求值类型为字符串
  4. cat_id: xxx, // 可选,表示新增item的类目
  5. item_info: xxx // 可选,值类型可以为json对象或字符串,不传默认为“”
  6. }。如已存在相同item_id,将直接覆盖该条记录。
  7. curl -X POST -d '{"item_id":"438","cat_id":19,"item_info":"dtboost.aliyun.com/re is the best recommendation service i ever used"}' "${host}/${dplus_org_code}/re/doModify?biz_code=xxx&target=RecItemInfo&method=NEW"

推荐池中更新一个可推荐物品

  1. 要求post内容为json格式,结构为
  2. {
  3. item_id: xxx, // 必须,要求值类型为字符串
  4. item_info: xxx // 可选,值类型可以为json对象或字符串,不传默认为“”
  5. }。如item_id不存在,将创建一条新记录。
  6. curl -X POST -d '{"item_id":"438","item_info":"dtboost.aliyun.com/re is the best recommendation service i ever used"}' "${host}/${dplus_org_code}/re/doModify?biz_code=xxx&target=RecItemInfo&method=UPDATE"

推荐池中下线一组item_id

  1. 要求post内容为数组,结构为[item_id1, item_id2, ..., item_idn]。如item_id不存在,将直接跳过。
  2. 单次API请求传入数组长度不超过50,否则直接报错
  3. curl -X POST -d '["438","439"]' "${host}/${dplus_org_code}/re/doModify?biz_code=xxx&target=RecItemInfo&method=DELETE"

新增/更新默认推荐结果

  1. method=UPDATENEW,效果相同。要求post内容为数组,结构为二维数组[[item_id,attr1, ..., attrn], ... ,item_id,attr1, ..., attrn],如[["438",1.0],["437",0.9],["436",0.8]]。其中第一维的每一个实例表示一个推荐项,如["438",1.0],其中438第一位必填固定表示item_id,第2-n位可选,如这里的1.0表示该item的评分。
  2. curl -X POST -d '[["438",1.0],["437",0.9],["436",0.8]]' "${host}/${dplus_org_code}/re/doModify?biz_code=biz&scn_code=scn&target=DefaultRec&method=UPDATE"

下线默认推荐结果

  1. 由于是post方法,但这里无post数据,需要您post一个空串以正常访问API
  2. curl -X POST -d '' "${host}/${dplus_org_code}/re/doModify?biz_code=biz&scn_code=scn&target=DefaultRec&method=DELETE"

新增/更新一个基于TAG的推荐结果

  1. method=UPDATENEW,效果相同。要求post内容为json对象,结构为:
  2. {
  3. key: 'cn-beiijng', // 必选,要求值类型为字符串
  4. reclist: [["438",1.0],["437",0.9],["436",0.8]] // 必选,要求值类型为数组,结构同默认推荐结果
  5. }
  6. curl -X POST -d '{"key":"cn-beiijng","reclist":[["438",1],["437",0.9],["436",0.8]]}' "${host}/${dplus_org_code}/re/doModify?biz_code=biz&scn_code=scn&path_code=path&target=TagBaseRec&method=UPDATE"

下线一组基于TAG的推荐结果

  1. 要求post内容为数组,结构为[tag1,tag2,...,tagn]。如tag不存在,将直接跳过。
  2. 单次API请求传入数组长度不超过50,否则直接报错
  3. curl -X POST -d '["cn-beijing","cn-hangzhou"]' "${host}/${dplus_org_code}/re/doModify?biz_code=biz&scn_code=scn&path_code=path&target=TagBaseRec&method=DELETE"

返回示例:

  1. 调用API成功
  2. {
  3. "code":"SUCCESS",
  4. "data":'success'
  5. "message":null
  6. }
  7. 调用API失败
  8. {
  9. "code":"${ERROR_CODE}",
  10. "data":'success'
  11. "message":具体的错误提示信息
  12. }

7、实时行为日志上传API

接口说明:

实时行为日志上传,需要使用GZIP对数据进行压缩成字节数组后进行上传。行为日志的规范请参见 日志埋点规范

Token获取方式:在产品控制台页面点击左侧菜单”API调试“,选择”系统日志采集API“,在下方的“请求地址”里查看。

本接口调用方法请参考下文中的代码样例。

如您使用实时行为日志上传API,在新建业务或编辑已有业务时,必须在“配置业务数据表”部分,勾选“使用本产品日志API接收日志”。勾选了使用日志API后,推荐引擎将不再需要您自行ETL上传行为表数据到MaxCompute(原ODPS)中。

地址:

  1. https://${host}/${dplus_org_code}/re/uploadlog?businessName=recplat&customerName=${customerName}&token=${token}

Method: POST

URL参数:

参数 类型 是否必填 实例 默认值 描述
businessName string “recplat” 没有默认值 日志服务的业务名,推荐引擎固定为”recplat”
customerName string “log_test” 没有默认值 客户在推荐引擎中的业务code
token string “xxxx” 没有默认值 推荐引擎分配给客户的日志token

注意: 这三个参数必须拼接在URL后面

Body数据:

  • Body数据是使用GZIP对多条行为日志组成的JSONArray字符串压缩后的字节数组。
  • JSONAarry字符串:里面是多条日志,建议积累一段时间上传(要么隔一分钟上传一次,要么一次累计约2000~3000条上传)。一次数据限制在5M内(压缩前),必须进行GZIP压缩,并需要设置POST请求头的Content-Encoding为gzip。
  • JSONArray每个元素是一条行为日志, 每一条是可以是字符串格式也可以是JSONObject(当然也可以是别的对象,只要能够toString),日志规范参见 日志埋点规范中的[行为类]。

注意

  • 调用API的时候需要加上HASH签名进行鉴权,参见:https://help.aliyun.com/document_detail/30245.html

  • POST请求头的Content-Encoding需要设置为gzip,不然服务器会拒收。

  • 上传的时候需要积累一定量再进行上传,要么累计一分钟,要么累计2000-3000条。

JAVA样例:

  1. package com.aliyun.datae.example;
  2. import java.io.BufferedReader;
  3. import java.io.ByteArrayOutputStream;
  4. import java.io.DataOutputStream;
  5. import java.io.IOException;
  6. import java.io.InputStreamReader;
  7. import java.net.MalformedURLException;
  8. import java.net.URL;
  9. import java.security.MessageDigest;
  10. import java.text.SimpleDateFormat;
  11. import java.util.Date;
  12. import java.util.LinkedList;
  13. import java.util.List;
  14. import java.util.Locale;
  15. import java.util.zip.GZIPOutputStream;
  16. import javax.crypto.Mac;
  17. import javax.crypto.spec.SecretKeySpec;
  18. import javax.net.ssl.HttpsURLConnection;
  19. import org.json.JSONArray;
  20. import org.json.JSONObject;
  21. import sun.misc.BASE64Encoder;
  22. public class UploadLogExample {
  23. // 用户需要修改
  24. private static String business_name = "businessName";
  25. private static String customer_name = "customerName";
  26. private static String token = "token";
  27. private static String url_prefix = "https://${host}/${dplus_org_code}/re/uploadlog?";
  28. // 用户需要修改, 用户的aid与ak
  29. private static String access_id ="accessId";
  30. private static String access_key ="accessKey";
  31. public static void main(String[] args) throws IOException{
  32. // 用户的logs
  33. // 每条日志是字符串
  34. List<String> logs = new LinkedList<String>();
  35. // 或者每条日志是JSONObect
  36. // List<JSONObject> logs = new LinkedList<JSONObject>();
  37. // 用户添加日志,要么积累一分钟的量, 要么积累到2000~3000条
  38. // logs.add(...)
  39. if(logs == null || logs.size() == 0){
  40. return;
  41. }
  42. // 积累的多条日志JSONArray数组
  43. JSONArray content = new JSONArray();
  44. // 用户向content中填写多条日志(要么积累一分钟的量, 要么积累到2000~3000条)
  45. for(String log : logs){
  46. content.put(log);
  47. }
  48. // 或者
  49. /*
  50. for(JSONObject log : logs){
  51. content.put(log);
  52. }
  53. */
  54. // 上传日志
  55. sendPostHTTPS(content, access_id, access_key);
  56. }
  57. /**
  58. * 使用HTTPS对多条日志进行GZIP压缩并上传
  59. * @param content 日志JSONArray数组(要么积累一分钟的量, 要么积累到2000~3000条)
  60. * @param ak_id
  61. * @param ak_secret
  62. * @throws IOException
  63. */
  64. public static void sendPostHTTPS(JSONArray content, String ak_id, String ak_secret) throws IOException
  65. {
  66. if(content != null && content.length() > 0){
  67. HttpsURLConnection conn = null;
  68. try {
  69. BufferedReader in = null;
  70. URL upload_url = new URL(url_prefix+"businessName=recplat&customerName="+customer_name+"&token="+token);
  71. StringBuilder result = new StringBuilder();
  72. /*
  73. * http header 参数 必须设置
  74. */
  75. String method = "POST";
  76. String accept = "application/json";
  77. String content_type = "application/json";
  78. String path = upload_url.getFile();
  79. String date = toGMTString(new Date());
  80. String content_encoding = "gzip";
  81. // 1. 对日志JSONArray进行FGZIP压缩
  82. byte[] body = compressToByte(content);
  83. // 2. 对body做MD5+BASE64加密
  84. String body_md5 = MD5Base64(body);
  85. String string_to_sign = method + "\n" + accept + "\n" + body_md5 + "\n" + content_type + "\n" + date + "\n" + path;
  86. // 3.计算 HMAC-SHA1
  87. String signature = HMACSha1(string_to_sign, ak_secret);
  88. // 4.得到 authorization header
  89. String auth_header = "Dataplus " + ak_id + ":" + signature;
  90. // 发起连接
  91. conn = (HttpsURLConnection) upload_url.openConnection();
  92. // 设置超时, 建议1分钟, 可以更大一点
  93. conn.setConnectTimeout(60000);
  94. conn.setReadTimeout(60000);
  95. // 设置请求方法
  96. conn.setRequestMethod("POST");
  97. // 设置通用的请求属性
  98. conn.setRequestProperty("accept", accept);
  99. conn.setRequestProperty("content-type", content_type);
  100. conn.setRequestProperty("date", date);
  101. conn.setRequestProperty("Authorization", auth_header);
  102. // 必须要设置为GZIP,否则服务器会不接受
  103. conn.setRequestProperty("Content-Encoding", content_encoding);
  104. // 发送POST请求必须设置如下两行
  105. conn.setDoOutput(true);
  106. conn.setDoInput(true);
  107. // 设置是非缓存
  108. conn.setUseCaches(false);
  109. // 传输body流 必须GZIP加密后字节数组
  110. DataOutputStream data_stream = new DataOutputStream(conn.getOutputStream());
  111. data_stream.write(body);
  112. data_stream.flush();
  113. data_stream.close();
  114. if (conn.getResponseCode() != 200) {
  115. System.err.println("LogUpload连接不成功!");
  116. }else{
  117. // 返回jsonobject
  118. in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
  119. String line = null;
  120. while ((line = in.readLine()) != null) {
  121. result.append(line);
  122. }
  123. if(result .length() > 0){
  124. JSONObject rsp_json = new JSONObject(result.toString());
  125. int success = rsp_json.getInt("success");
  126. if(success == 1){
  127. System.out.println("数据上传成功, msg: " + rsp_json.getString("errMsg"));
  128. }else{
  129. System.out.println("数据上传失败, 出错信息为: " + rsp_json.getString("errMsg"));
  130. }
  131. }
  132. }
  133. } catch (MalformedURLException e) {
  134. e.printStackTrace();
  135. } catch (IOException e) {
  136. e.printStackTrace();
  137. } finally {
  138. if (conn != null) {
  139. conn.disconnect();
  140. }
  141. }
  142. }
  143. }
  144. /*
  145. * 计算MD5+BASE64
  146. */
  147. public static String MD5Base64(byte[] utfBytes) {
  148. if (utfBytes == null)
  149. return null;
  150. String encodeStr = "";
  151. MessageDigest mdTemp;
  152. try {
  153. mdTemp = MessageDigest.getInstance("MD5");
  154. mdTemp.update(utfBytes);
  155. byte[] md5Bytes = mdTemp.digest();
  156. BASE64Encoder b64Encoder = new BASE64Encoder();
  157. encodeStr = b64Encoder.encode(md5Bytes);
  158. } catch (Exception e) {
  159. throw new Error("Failed to generate MD5 : " + e.getMessage());
  160. }
  161. return encodeStr;
  162. }
  163. /*
  164. * 计算 HMAC-SHA1
  165. */
  166. public static String HMACSha1(String data, String key) {
  167. String result;
  168. try {
  169. SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
  170. Mac mac = Mac.getInstance("HmacSHA1");
  171. mac.init(signingKey);
  172. byte[] rawHmac = mac.doFinal(data.getBytes());
  173. result = (new BASE64Encoder()).encode(rawHmac);
  174. } catch (Exception e) {
  175. throw new Error("Failed to generate HMAC : " + e.getMessage());
  176. }
  177. return result;
  178. }
  179. /*
  180. * 等同于javaScript中的 new Date().toUTCString();
  181. */
  182. public static String toGMTString(Date date) {
  183. SimpleDateFormat df = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z", Locale.UK);
  184. df.setTimeZone(new java.util.SimpleTimeZone(0, "GMT"));
  185. return df.format(date);
  186. }
  187. /*
  188. * 字符串压缩为字节数组
  189. */
  190. public static byte[] compressToByte(JSONArray content){
  191. try{
  192. int len1 = content.toString().getBytes("utf-8").length;
  193. System.out.println("压缩前数据大小(B): " + len1);
  194. ByteArrayOutputStream out = new ByteArrayOutputStream();
  195. GZIPOutputStream gzip = new GZIPOutputStream(out);
  196. gzip.write(content.toString().getBytes("utf-8"));
  197. gzip.finish();
  198. gzip.flush();
  199. gzip.close();
  200. byte[] data = out.toByteArray();
  201. int len2 = data.length;
  202. System.out.println("压缩后数据大小(B): " + len2);
  203. System.out.println("压缩率: " + len2*1.0/len1);
  204. return data;
  205. } catch(Exception e){
  206. e.printStackTrace();
  207. return null;
  208. }
  209. }
  210. }

NodeJS样例

  1. var request = require('request');
  2. var url = require('url');
  3. var crypto = require('crypto');
  4. var zlib = require('zlib');
  5. // 日志上传请求url前半部分, 后面需要跟上businessName、customerName与token三个参数字段
  6. var url_prefix = "https://${host}/${dplus_org_code}/re/uploadlog?";
  7. // 拼接在url后面的三个参数, 客户需要自己填写
  8. var business_name = "recplat";
  9. var customer_name = "customerName";
  10. var token = "token";
  11. // 客户填写自己的accessId与accessKey
  12. var ak_id = 'accessId';
  13. var ak_key = 'accessKey';
  14. /**
  15. 注意事项:
  16. 首先, 每条日志可以是String格式, 也可以是JSONObject, 当然也可以是别的对象, 只要每条日志能够toString
  17. 然后, 将每条日志put到JSONArray中, 需要积累一段时间(如一分钟)或者一定量的日志再上传(2000~3000条)
  18. 接着, 将JSONArray转换为String, 并使用gzip进行压缩, 将压缩后的数据作为body上传
  19. 最后, 使用数加鉴权中的那些字段进行签名
  20. 其中, 请求头中的Content-Encoding不参与签名,但是必须设置为gzip
  21. **/
  22. // 每条日志可以是String格式, 也可以是JSONObject, 只要每条日志能够toString
  23. // 这是一条JSONObject日志数据, 可以是JSONObject对象
  24. var log = eval('({"user_id":"xxx",'+
  25. '"item_id":0, "action":"buy",'+
  26. '"bhv_datetime" : "2016-05-26 00:00:00", "app_version":"1.0.1"})');
  27. // 也可以是String
  28. /*
  29. var log = '{"user_id":"xxx",'+
  30. '"item_id":0, "action":"view",'+
  31. '"bhv_datetime" : "2016-05-26 00:00:00", "app_version":"1.0.1"}';
  32. */
  33. // 多条行为日志组成JSONArray
  34. var log_json_array = [];
  35. // 多条日志put到JSONArray中, 这里造2000条数据
  36. for(var i = 0; i < 2000; ++i){
  37. log_json_array.push(log_str);
  38. }
  39. // JSONArray 转换成String
  40. var logs = JSON.stringify(log_json_array);
  41. // send post函数
  42. sendPostHttps = function(logs,a_id,a_key) {
  43. // 请求方式, 签名字段之一
  44. var method = "POST";
  45. /*
  46. * http header 参数 必须设置, 五个
  47. */
  48. // 请求返回的是JSONObject, 签名字段之一
  49. var accept = "json";
  50. // 编码是json, 签名字段之一
  51. var content_type = "application/json";
  52. // 上传时间, 签名字段之一
  53. var date = new Date().toUTCString();
  54. // 上传的数据是gzip压缩的, 不参与签名
  55. var content_encoding = "gzip";
  56. // url
  57. var log_upload_url = url_prefix+'businessName='+business_name+'&customerName='+customer_name+'&token='+token;
  58. // url的path, 签名字段之一
  59. var path = url.parse(log_upload_url).path;
  60. // 请求选项, 这些字段必须填写
  61. var options = {
  62. url: log_upload_url,
  63. method: method,
  64. body: '',
  65. headers: {
  66. 'accept': accept,
  67. 'content-type': content_type,
  68. 'Content-Encoding':content_encoding,
  69. 'date': date,
  70. 'Authorization': ''
  71. }
  72. };
  73. // step 1 使用gzip进行压缩
  74. zlib.gzip(logs,function(err, buffer) {
  75. if (!err) {
  76. // 使用压缩后的数据填充请求body
  77. options.body = buffer;
  78. console.log("step1 Gzip body", buffer);
  79. // step2: 组stringToSign [StringToSign = #{method}\\n#{accept}\\n#{md5(body)}\\n#{contentType}\\n#{date}\\n#{action}]
  80. var bodymd5 = md5(buffer);
  81. var string_to_sign = options.method + "\n" + options.headers.accept + "\n" + bodymd5 + "\n" + options.headers['content-type'] + "\n" + options.headers.date + "\n" + path;
  82. console.log("step2 Sign string:", string_to_sign);
  83. // step3: 加密 [Signature = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign)))]
  84. var signature = sha1(string_to_sign, ak_key);
  85. console.log("step3 Signature:", signature);
  86. // step4: 组authorization header [Authorization = Dataplus AccessKeyId + ":" + Signature]
  87. var auth_header = "Dataplus " + ak_id + ":" + signature;
  88. console.log("step4 Authorization Header:", auth_header);
  89. options.headers.Authorization = auth_header;
  90. // step5: send request
  91. function callback(error, response, body) {
  92. if (error) {
  93. console.log("error", error);
  94. }else {
  95. console.log("step5 response body:", response.statusCode, body);
  96. }
  97. }
  98. request(options, callback);
  99. }
  100. });
  101. };
  102. // 计算MD5+BASE64
  103. md5 = function(buffer) {
  104. var hash;
  105. hash = crypto.createHash('md5');
  106. hash.update(buffer);
  107. return hash.digest('base64');
  108. };
  109. // 计算 HMAC-SHA1
  110. sha1 = function(string_to_sign, secret) {
  111. var signature;
  112. return signature = crypto.createHmac('sha1', secret).update(string_to_sign).digest().toString('base64');
  113. };
  114. // 最后调用函数发送请求
  115. sendPostHttps(logs,ak_id,ak_key);

返回示例:

  1. {
  2. "success":1,
  3. "errMsg":""
  4. }
本文导读目录