相关文章推荐
仗义的稀饭  ·  delphi ...·  1 年前    · 
坚强的打火机  ·  How to filter array ...·  1 年前    · 
HTTP API

HTTP API

更新时间:

服务端埋点参数获取

  1. 进入管理控制台-->点击采集信息

  2. 主域名:服务端埋点数据上报地址

  3. 副域名:默认和主域名一致,私部署客户可以提供副域名作为备用域名

  4. ServiceSecret和ServiceID为校验参数,注意保密不可泄露。

注意:因为上述信息涉及系统安全,所以只有用管理员权限才可以看到!

image.png

请求

接口地址:https://<收数域名+端口>/server

请求参数content-type:application/json

请求参数

上报事件请求JSON参数

JSON字段名

(大小写也许保持完全一致)

数据类型

是否必传

字段描述

示例

sign

string

校验签名

f564cae6a8ad458648id9d607a124322

app_id

string

对应服务端埋点中的 serviceID

OA8kI9Jis7YJNh5uh

appkey

string

应用key,从QT管理后台获取

9moqdsuia8hvxm7k8shf82n

id

string

事件编码,用户在QT后台创建事件时设置的编码

click

umid

string

二者必须至少有一个上传,建议可以获取puid就尽量上传,保证分析时的IDMapping符合业务需求

设备ID

djajdjdk1

puid

string

用户ID

user_001

page_name

string

页面名称

page_index

ts

string

上报的毫秒级时间戳

1659493170125

cusp

object

事件属性

{"foo": "bar"}

gp

object

全局属性

{"foo": "bar"}

sdk_type

string

SDK类型,固定值为httpapi

httpapi

server_ts

string

否,QT服务端可自动生成。 若您手动上传,则以您上传的服务端时间为准,QT服务端不再自动生成。

服务端毫秒级时间戳

1659493170125

uuid

string

否, 生成log_id的因子(2.4.17版本支持)

如果希望log_id可以有较强的唯一性,可以为每条日志上报唯一的uuid,QT服务端将使用该uuid生成log_id

1234567890

log_id

string

否,QT服务端自动生成

日志标识,默认生成方式为uuid生成,若uuid没有上报,则使用时间戳生成随机id。

1234567890

事件 报文协议

{ 
  "sign":"27c95768438645138cfb010ded871091",  //必填,签名
  "app_id":"xxxxxxxxx",                       //对应服务端埋点信息中的:ServiceID
  "appkey":"1344444",                         //必填,应用appkey
  "id" : "get_coupons",                       //必填,事件编码
  "umid" : "edfafs",                          //设备id
  "puid" : "123456",                          //登录用户id
                                              //umid和puid务必至少上传一个
  "page_name":"home_page",                    //选填,页面编码 
  "ts" : "1614667799165",                     //必填,事件发生的时间戳
  "cusp" : {                                  //事件自定义参数
    "p1" : "1",
    "p2" : "2",
    "p3" : "3"
  "sdk_type":"httpapi",                       // 固定值,采集服务自动加上该字段
  "server_ts":"1614667799165",               // 如果主动传入,会使用传入值,不传入会自动使用采集服务采集日志时间戳,采集服务自动加上该字段
  "_id":'123344'                              //日志唯一编码,采集服务自动加上该字段
}

上报用户属性请求JSON参数

JSON字段名

(大小写也需保持完全一致)

数据类型

是否必传

字段描述

示例

sign

string

校验签名

f564cae6a8ad458648id9d607a124322

app_id

string

对应服务端埋点中的 serviceID

OA8kI9Jis7YJNh5uh

appkey

string

应用key,从QT管理后台获取

9moqdsuia8hvxm7k8shf82n

id

string

上报用户属性的事件编码,固定为$$_user_profile

$$_user_profile

umid

string

设备ID

djajdjdk1

puid

string

用户ID

user_001

ts

string

上报的毫秒级时间戳

1659493170125

cusp

object

用户属性

{"name": "tom", "gender": "1"}

sdk_type

string

SDK类型,固定值为httpapi

httpapi

server_ts

string

否,QT服务端可自动生成。 若您手动上传,则以您上传的服务端时间为准,QT服务端不再自动生成。

服务端毫秒级时间戳

1659493170125

uuid

string

否, 生成log_id的因子(2.4.17版本支持)

如果希望log_id可以有较强的唯一性,可以为每条日志上报唯一的uuid,QT服务端将使用该uuid生成log_id

1234567890

log_id

string

否,QT服务端自动生成

日志标识,默认生成方式为uuid生成,若uuid没有上报,则使用时间戳生成随机id。

1234567890

用户属性 报文协议

{ 
  "sign":"27c95768438645138cfb010ded871091",  //必填,签名
  "app_id":"xxxxxxxxx",                       //必填,对应服务端埋点信息中的:ServiceID
  "appkey":"1344444",                         //必填,应用appkey
  "id" : "$$_user_profile",                   //必填,标识为用户属性
  "umid" : "edfafs",                          //选填,设备id
  "puid" : "123456",                          //必填,登录用户账号ID
  "ts" : "1614667799165",                     //必填,事件发生的时间戳
  "cusp" : {                                  //必填,自定义用户属性
    "gender" : "1",                           //性别
    "birthday" : "1988-12-24"                 //出生年月
  "sdk_type" : "httpapi",                     //固定值,采集服务自动加上该字段
  "server_ts":"1614667799165",             // 如果主动传入,会使用传入值,不传入会自动使用采集服务采集日志时间戳,采集服务自动加上该字段
  "_id":'123344'                           //日志唯一编码,采集服务自动加上该字段
}

sign生成规则说明

  1. 先将除了sign字段的其他所有请求字段的key和value封装成一个map对象

  2. 将这个map对象序列化成JSON字符串,生成时需要将key按照自然排序即ascii码排序,java可使用JSONObject.toJSONString(mapObject, SerializerFeature.MapSortField),(注:json需要进行压缩,去掉个字段之间的空格,如后面的代码块)

  3. 获取 serviceSecret

  4. sign = MD5(排序完生成的JSON字符串 + serviceSecret)

  5. 将计算得到的sign放到原始的map对象中,再序列化成JSON字符串即为最终完整的JSON请求报文

sign校验流程图

image

响应说明

响应数据content-type:application/json

响应的JSON字符串中有两个字段,分别为code、message

code

message

描述

Httpapi_300_200

上报成功

成功

Httpapi_300_101

非法的签名

签名校验失败,请参考sign生成规则说明

Httpapi_300_102

上报的数据类型非JSON格式

请求参数的数据格式不是JSON格式

Httpapi_300_103

缺少必要字段

参考请求参数表,检查是否遗漏了必填的参数

Httpapi_300_104

用户属性缺少必要字段

参考请求参数表,检查是否遗漏了必填的参数

Httpapi_300_105

非法事件ID

事件ID不正确

Httpapi_300_106

ak/sk不正确

Java Demo参考

package com.aliyun.app.quickaplus.mock.once_mock;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.aliyun.app.quickaplus.mock.util.MD5Util;
import okhttp3.*;
import java.io.IOException;
* 服务端ApiDemo
* @author chengtao
public class ServiceApiDemo {
    * 主域名
    private static final String API_URL = "*****";
    * 对应服务端埋点信息中的:ServiceID
    private static final String SERVICE_ID = "*******";
    * 对应服务端埋点信息中的:ServiceSecret
    private static final String SERVICE_SECRET = "*******";
    * client日志
    private static final OkHttpClient client = new OkHttpClient();
* 主函数
* @param args 参数
    public static void main(String[] args) {
        String srcJsonString = getMockData();
        JSONObject json = JSON.parseObject(srcJsonString);
        //向报文中添加服务ID
        json.put("app_id",SERVICE_ID);
        //向报文中添加ts
        json.put("ts", String.valueOf(System.currentTimeMillis()));
        //计算签名
        String sign = MD5Util.md5(JSONObject.toJSONString(json,
        SerializerFeature.MapSortField) + SERVICE_SECRET);
        json.put("sign", sign);
        String desJsonString = JSON.toJSONString(json,
        SerializerFeature.DisableCircularReferenceDetect);
        Request request = new Request.Builder()
        .url(API_URL + "/server")
        .post(RequestBody.create(desJsonString,
        MediaType.parse("application/json")))
        .build();
        try {
            Response response = client.newCall(request).execute();
            if (!response.isSuccessful()) {
                System.out.printf("[DEMO] 发送日志失败 %s%n", response);
            } else {
                System.out.printf("[DEMO] 发送成功 %s%n",
                response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
* 获取MockData
* @return 返回值
    private static String getMockData() {
        return "{\n" +
        " \"appkey\": \"1344444\",\n" +
        " \"id\": \"get_coupons\",\n" +
        " \"umid\": \"uuid()\",\n" +
        " \"puid\": \"puid1\",\n" +
        " \"page_name\": \"home_page\",\n" +
        " \"cusp\": {\n" +
        " \"p1\": \"1\",\n" +
        " \"p2\": \"2\",\n" +
        " \"p3\": \"3\"\n" +
        " },\n" +
        " \"gp\": {\n" +
        " \"p1\": \"1\",\n" +
        " \"p2\": \"2\",\n" +
        " \"p3\": \"3\"\n" +
        " },\n" +
        " \"sdk_type\": \"httpapi\"\n" +
}

Python Demo 参考

# 正确的签名格式
{"appkey": "4b6G49PAkLUb4212","cusp":{"p1":"1","p2":"2","p3":"3"},"gp":{"p1":"1","p2":"2","p3":"3"},"id":"get_coupons","page_name":"home_page","puid":"puid1","sdk_type":"httpapi","umid":"uuid()"}tEkNnx8VDuR0mwEl3hXd7aozYh8Q2qS4
# 错误的签名格式
{"appkey": "您环境下的AppKey", "cusp": {"p1": "1", "p2": "2", "p3": "3"}, "gp": {"p1": "1", "p2": "2", "p3": "3"}, "id": "get_coupons", "page_name": "home_page", "puid": "puid1", "sdk_type": "httpapi", "umid": "uuid()"}tEkNnx8VDuR0mwEl3hXd7aozYh8Q2qS4

import json
import requests
import hashlib
# service_id
service_id = 您环境下的service_id
# service_secret
service_secret = 您环境下的service_secret
# 收数服务配置
qlc_end_point = 您环境下的收数服务地址
# Mock的报文
data = {
    "app_id": service_id,
    "id": "get_coupons",
    "umid": "uuid1",
    "puid": "puid1",
    "page_name": "home_page",
    "ts": 123,
    "cusp": {
        "p1": "1",
        "p2": "2",
        "p3": "3"
    "gp": {
        "p1": "1",
        "p2": "2",
        "p3": "3"
    "sdk_type": "httpapi"
# 按照key的字典序排序
sorted_data = sorted(data.items(), key=lambda item: item[0])
sorted_dict = dict(sorted_data)
string = json.dumps(sorted_dict, separators=(',', ':')) + service_secret
data['sign'] = hashlib.md5(string.encode('utf-8')).hexdigest()
response = requests.post(qlc_end_point + "/server", json=data)
print(response.text)

MD5Util类代码

package com.aliyun.app.quickaplus.mock.util;
import java.security.MessageDigest;
 * md5加密工具类
 * @author zheng.zhang
public class MD5Util {
    public static String md5(String s) {
        char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        try {
            byte[] btInput = s.getBytes();
            // 获得MD5摘要算法的 MessageDigest 对象
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            // 使用指定的字节更新摘要
            mdInst.update(btInput);
            // 获得密文
            byte[] md = mdInst.digest();
            // 把密文转换成十六进制的字符串形式
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            return new String(str);
        } catch (Exception e) {
            return null;
     * 单元测试
    public static void main(String[] args) {
        StringBuilder dtskey = new StringBuilder("abc12390");
        dtskey.append('\0').append("~!@#$%^&*()_+");
        System.out.println(MD5Util.md5(dtskey.toString()));
        System.out.println(MD5Util.md5("20121221"));
        System.out.println(MD5Util.md5("加密"));