在与公司外部系统对接时,API接口一般采用REST风格,对外暴露HTTP服务。只需要将入参封装好,并发起HTTP请求即可。具体请求流程如下图所示:

数据格式

API调用参数分为系统参数和业务参数,请求时,系统参数是必传的,否则无法成功请求,业务参数由具体业务接口定义。

系统参数
名称 类型 必填 描述
apiKey String 分配给供应商的唯一身份标识
sign String 请求签名,生成规则参见签名机制
timestamp String 时间戳,参见时间戳 

API请求返回结果目前支持json和xml格式,具体返回格式由请求头中的Content-Type属性来决定。当Content-Type属性为application/xml时,返回xml格式,其余情况下统一返回json格式。返回结果包含以下字段

系统参数
名称 类型 必填 描述
returnCode int 结果码,具体值参见API返回码
errorMsg String 异常时错误信息
data String 返回结果
success boolean 是否请求成功

请求示例

我们以团期信息管理接口为例,假设我们需要维护团期,接口请求原始入参(业务参数+系统参数)如下.

{
"apiKey": "testApiKey",//系统参数
"timestamp": "2015-07-30 12:34:56",//系统参数
"agencyProductId": "test10001",//业务参数
"groupNum": "",//业务参数
"planInfo": [//业务参数
{
"planDateStr": "2015-07-18",
"datePriceList": [
{
"schemeId": "scheme0001",
"scheduleId": "schedule",
"agencyBudget": 1000,
"agencyBudgetChild": 500,
"excludeChild": 1,
"roomAddBudget": 100,
"roomGapFlag": 1,
"aheaddate": 4,
"deadlinedate": 3,
"deadlinehour": 18,
"promoFlag": 1,
"setGroupFlag": 1,
"stuffEndDate": 5
}
]
}
]
}

首先,我们需要根据现有参数生成签名,签名生成步骤如下:

1. 将入参按照一级key值进行排序(按字典顺序进行排序,忽略大小写),去掉value值为空的入参,我们将得到以下格式参数(业务参数+系统参数)如下

{
"agencyProductId": "test10001",
"apiKey": "testApiKey",
"planInfo": [
{
"planDateStr": "2015-07-18",
"datePriceList": [
{
"schemeId": "scheme0001",
"scheduleId": "schedule",
"agencyBudget": 1000,
"agencyBudgetChild": 500,
"excludeChild": 1,
"roomAddBudget": 100,
"roomGapFlag": 1,
"aheaddate": 4,
"deadlinedate": 3,
"deadlinehour": 18,
"promoFlag": 1,
"setGroupFlag": 1,
"stuffEndDate": 5
}
]
}
],
"timestamp": "2015-07-30 12:34:56"
}

2. 获取一级key和对应的value,例如上面参数中planInfo键,对应的值为[{"planDateStr":"2015-07-18","datePriceList":[{"schemeId":"scheme0001","scheduleId":"schedule","agencyBudget":1000,"agencyBudgetChild":500,"excludeChild":1,"roomAddBudget":100,"roomGapFlag":1,"aheaddate":4,"deadlinedate":3,"deadlinehour":18,"promoFlag":1,"setGroupFlag":1,"stuffEndDate":5}]}], 以字符串的形式把key+value拼接起来得到一个新的字符串(二级三级键不进行操作),如下:

planInfo[{"planDateStr":"2015-07-18","datePriceList":[{"schemeId":"scheme0001","scheduleId":"schedule","agencyBudget":1000,
"agencyBudgetChild":500,"excludeChild":1,"roomAddBudget":100,"roomGapFlag":1,"aheaddate":4,"deadlinedate":3,"deadlinehour":18,
"promoFlag":1,"setGroupFlag":1,"stuffEndDate":5}]}]

其他键值对同样如此,将拼装好的字符串再依次拼接起来,如下:

agencyProductIdtest10001apiKeytestApiKeyplanInfo[{"planDateStr":"2015-07-18","datePriceList":[{"schemeId":"scheme0001","scheduleId":"schedule",
"agencyBudget":1000,"agencyBudgetChild":500,"excludeChild":1,"roomAddBudget":100,"roomGapFlag":1,"aheaddate":4,"deadlinedate":3,"deadlinehour":18,
"promoFlag":1,"setGroupFlag":1,"stuffEndDate":5}]}]timestamp2015-07-30 12:34:56

3. 在拼好的字符串前后都加上签名密钥,我们假设密钥是ZbWjUMYevqT9Tnup4jRs,可以得到以下字符串:

ZbWjUMYevqT9Tnup4jRsagencyProductIdtest10001apiKeytestApiKeyplanInfo[{"planDateStr":"2015-07-18","datePriceList":[{"schemeId":"scheme0001",
"scheduleId":"schedule","agencyBudget":1000,"agencyBudgetChild":500,"excludeChild":1,"roomAddBudget":100,"roomGapFlag":1,"aheaddate":4,
"deadlinedate":3,"deadlinehour":18,"promoFlag":1,"setGroupFlag":1,"stuffEndDate":5}]}]timestamp2015-07-30 12:34:56ZbWjUMYevqT9Tnup4jRs

4. 对生成的字符串进行MD5加密,并将结果全部转为大写,获得签名值:

85F60EFE28BB4688F3BA4A37FF62C101

5. 将签名加入到入参中:

{
"agencyProductId": "test10001",
"apiKey": "testApiKey",
"planInfo": [
{
"planDateStr": "2015-07-18",
"datePriceList": [
{
"schemeId": "scheme0001",
"scheduleId": "schedule",
"agencyBudget": 1000,
"agencyBudgetChild": 500,
"excludeChild": 1,
"roomAddBudget": 100,
"roomGapFlag": 1,
"aheaddate": 4,
"deadlinedate": 3,
"deadlinehour": 18,
"promoFlag": 1,
"setGroupFlag": 1,
"stuffEndDate": 5
}
]
}
],
"timestamp": "2015-07-30 12:34:56",
"sign": "85F60EFE28BB4688F3BA4A37FF62C101"
}

6.发起HTTP请求。

附上MD5加密方法参考示例:

private static String Md5Encode(String str) throws NoSuchAlgorithmException {
StringBuilder sign = new StringBuilder(); MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytes = md.digest(str.getBytes()); for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
sign.append("0");
}
sign.append(hex.toUpperCase());
}
return sign.toString();
}

在spring-core包中提供了一个MD5加密工具!

org.springframework.util.DigestUtils#md5DigestAsHex(byte[])

注意事项

1. 所有的请求和响应数据编码皆为utf-8格式

2. 生成签名时,空值的参数不参与校验

3. 生成签名时,参数名称和值大小写敏感

4. 排序规则为按字典顺序进行排序,忽略大小写

附代码实现:

public class APITest {
public static void main(String[] args) throws NoSuchAlgorithmException {
String s = "{\n" +
" \"apiKey\": \"testApiKey\",\n" +
" \"timestamp\": \"2015-07-30 12:34:56\",\n" +
" \"agencyProductId\": \"test10001\",\n" +
" \"groupNum\": \"\",\n" +
" \"planInfo\": [\n" +
" {\n" +
" \"planDateStr\": \"2015-07-18\",\n" +
" \"datePriceList\": [\n" +
" {\n" +
" \"schemeId\": \"scheme0001\",\n" +
" \"scheduleId\": \"schedule\",\n" +
" \"agencyBudget\": 1000,\n" +
" \"agencyBudgetChild\": 500,\n" +
" \"excludeChild\": 1,\n" +
" \"roomAddBudget\": 100,\n" +
" \"roomGapFlag\": 1,\n" +
" \"aheaddate\": 4,\n" +
" \"deadlinedate\": 3,\n" +
" \"deadlinehour\": 18,\n" +
" \"promoFlag\": 1,\n" +
" \"setGroupFlag\": 1,\n" +
" \"stuffEndDate\": 5\n" +
" }\n" +
" ]\n" +
" }\n" +
" ]\n" +
"}";
String signature = getSignature(s, "ZbWjUMYevqT9Tnup4jRs");
System.out.println(Md5Encode(signature));
} public static String getSignature(String requestData, String secretKey) {
//第一步,获取所有值非空的key
List<String> keyList = new ArrayList<String>();
Map<String,Object> data = JsonUtil.toBean(requestData,Map.class);
for (String key : data.keySet()) {
if (key == null) {
continue;
}
//value为null或空
if (data.get(key) == null || StringUtils.isBlank(data.get(key).toString())) {
continue;
}
keyList.add(key);
}
//按名称排序并拼接成字符串
String[] arrayToSort = keyList.toArray(new String[keyList.size()]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);

StringBuilder sb = new StringBuilder(secretKey);
for (String key : arrayToSort) {
sb.append(key);
sb.append(JsonUtil.toString(data.get(key)));
}
sb.append(secretKey);
System.out.println(sb.toString());
return sb.toString();
} /**
* MD5 加密
* @param str 需要加密的字符串
* @return 经过加密的字符串
* @throws NoSuchAlgorithmException
*/
private static String Md5Encode(String str) throws NoSuchAlgorithmException {
StringBuilder sign = new StringBuilder();
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytes = md.digest(str.getBytes()); for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
sign.append("0");
}
sign.append(hex.toUpperCase());
}
return sign.toString();
}
}

系统对接API调用的更多相关文章

  1. WHMCS系统API调用

    WHMCS:域名管理系统,现在网络上很多借助此系统Shadowsocks插件+ShadowsocksR多用户服务端进行VPN的售卖,能做到流量控制等. 在对接此系统的API时,我发现了很多功能都已经实 ...

  2. 反射调用android系统级API函数

    try { Class<?> mClass = Class.forName("com.android.server.wifi.WifiSettingsStore"); ...

  3. 使用ctypes调用系统C API函数需要注意的问题,函数参数中有指针或结构体的情况下最好不要修改argtypes

    有人向我反应,在代码里同时用我的python模块uiautomation和其它另一个模块后,脚本运行时会报错,但单独使用任意一个模块时都是正常的,没有错误.issue链接 我用一个例子来演示下这个问题 ...

  4. 基于IdentityServer的系统对接微信公众号

    业务需求 公司有两个业务系统,A和B,AB用户之间属于多对一的关系,数据库里面也就是两张表,A表有个外键指向B.现在需要实现以下几个功能. A用户扫描B的二维码,填写相关的注册信息,注册完成之后自动属 ...

  5. java接口对接——别人调用我们接口获取数据

    java接口对接——别人调用我们接口获取数据,我们需要在我们系统中开发几个接口,给对方接口规范文档,包括访问我们的接口地址,以及入参名称和格式,还有我们的返回的状态的情况, 接口代码: package ...

  6. FormatMessage与GetLastError配合使用,排查windows api调用过程中的错误

    前一段时间在学习windows api调用过程中,遇到过一些调用错误或者程序没能显示预期的结果,或者直接出现vc运行时错误. 这对新手来说是司空见惯的事,因为不太熟悉难免会出错,出错的信息如果能显示很 ...

  7. WPF技术触屏上的应用系列(二): 嵌入百度地图、API调用及结合本地数据库在地图上进行自定义标点的实现

    原文:WPF技术触屏上的应用系列(二): 嵌入百度地图.API调用及结合本地数据库在地图上进行自定义标点的实现 去年某客户单位要做个大屏触屏应用,要对档案资源进行展示之用.客户端是Window7操作系 ...

  8. Redis和nosql简介,api调用;Redis数据功能(String类型的数据处理);List数据结构(及Java调用处理);Hash数据结构;Set数据结构功能;sortedSet(有序集合)数

    1.Redis和nosql简介,api调用 14.1/ nosql介绍 NoSQL:一类新出现的数据库(not only sql),它的特点: 1.  不支持SQL语法 2.  存储结构跟传统关系型数 ...

  9. 华为云的API调用实践(python版本)

    一.结论: 1.华为云是符合openstack 社区的API,所以,以社区的API为准.社区API见下面的链接. https://developer.openstack.org/api-ref/net ...

随机推荐

  1. 【转】TCP建立连接三次握手和释放连接四次握手

    在谈及TCP建立连接和释放连接过程,先来简单认识一下TCP报文段首部格式的的几个名词(这里只是简单说明,具体请查看相关教程) 序列号seq:占4个字节,用来标记数据段的顺序,TCP把连接中发送的所有数 ...

  2. Asp.Net Core2.0允许跨域请求设置

    1.services /// <summary> /// /// </summary> /// <param name="services">& ...

  3. 人生第一个快速幂的题(HDU - 1097--A hard puzzle )

    题意: 最简单的快速幂.给你两个数n和m,求n^m的最后一位: 解题思路: 额,快速幂就很简单了,这里只要最后一位可以一对每次运算都%10: 代码: #include<cstdio> #i ...

  4. CSU - 2056 a simple game

    Description 这一天,小A和小B在玩一个游戏,他俩每人都有一个整数,然后两人轮流对他们的整数进行操作,每次在下列两个操作任选一个: (1)对整数进行翻转,如1234翻转成4321 ,1200 ...

  5. 今日头条高级后端开发实习生三轮技术面+HR面 面经

    二面结束后已经意识模糊,好多问过的东西都忘了,而且有一些基础知识就不在这写了,大部分公司都问的差不多... 一面(2018/03/27,11:00~11:50) 1:自我介绍 2:简单说说你这个项目吧 ...

  6. Java 异常处理之 论 finally块何时候不走

    一. exit退出异常: import java.util.Scanner; public class Test3exit { /** * @param 房山的猫 * finally什么时候不走 * ...

  7. 日报 18/07/15 Java 性能优化

    尽量指定类和方法的final修饰符 带有final修饰符的类是不可派生的 在java核心api中 有许多应用final的例子 例如 java.lang.string整个类都是final的 为类指定fi ...

  8. iOS Sprite Kit教程之使用帮助文档以及调试程序

    iOS Sprite Kit教程之使用帮助文档以及调试程序 IOS中使用帮助文档 在编写代码的时候,可能会遇到很多的方法.如果开发者对这些方法的功能,以及参数不是很了解,就可以使用帮助文档.那么帮助文 ...

  9. 特征向量、特征值以及降维方法(PCA、SVD、LDA)

    一.特征向量/特征值 Av = λv 如果把矩阵看作是一个运动,运动的方向叫做特征向量,运动的速度叫做特征值.对于上式,v为A矩阵的特征向量,λ为A矩阵的特征值. 假设:v不是A的速度(方向) 结果如 ...

  10. 选择排序之Java实现

    选择排序之Java实现 一.方法一 package cn.com.zfc.lesson21.sort; /** * * @title SelectSort * @describe 选择排序 * @au ...