前言:为自家零售行业的公司做OMS系统,需要通过奇门去对接第三方WMS系统。自家有淘宝店,申请商家自研应用还是很好申请的。就当前在做的OMS系统而言,我们要用的奇门的接口其实也就3个模块,发货,退换货,取消。介于官方接口那一堆要传的参数,我们还是选择用他们的SDK(.net core版本)。
对于官方SDK,咱还是100%信任的,出问题,首先咱就觉得是自己的问题,加班加点N次轮番测试,绞尽脑汁从自身找问题,快要崩溃的时候,扛不住了。
这里要感谢官方提供了SDK的源码,让我终于找出了问题所在。找到问题的那一瞬间,爆粗口 的有木有,气死人不偿命的有木有。我忍,呼~
故此篇不会叙述申请应用那块的流程,这个自己去百度或官方文档就行,只重点叙述对接过程的坑点
言归正传,进入接口对接主题,首先是先跟奇门测试环境进行自测,测试通过之后就进入跟第三方的联调。
接口统一参数说明:
奇门测试环境接口地址Url:http://qimen.api.taobao.com/top/router/qmtest
淘宝应用Appkey:你在淘宝开放平台申请的应用的Appkey
淘宝应用Secretkey:你在淘宝开放平台申请的应用的Secretkey
CustomerId:测试环境统一用mockCustomerId模拟测试,转正式的第三方对接的公司会提供
OwnerCode:货主编码,这是第三方公司需要的参数,他们会提供
对接SDK版本:.net core,自己系统框架:.net 5.0
以此接口为例taobao.qimen.deliveryorder.confirm(发货单已发货回传接口)
进入淘宝开放平台 - 奇门 - 三方互通 - 我的场景,点击 taobao.qimen.deliveryorder.confirm 接口 进入,填写自测地址(即奇门回传信息给我们的地址,这需要咱将回写接口发布并且可外网访问)。在“系统检测的API列表” 中找到 taobao.qimen.deliveryorder.confirm,点击“进入”,有5项是需要进行测试的, 直接点击 “自测” 按钮,奇门官方测试环境就会逐个发送 请求到你的回传接口那里,只要其中一个没校验成功便会中断后面的校验。
坑点:
1. 网络&返回格式验收,内容里写着:要求返回的内容是api指定的格式(json/xml),即可以返回JSON也可以XML
注意:只能返回XML格式它才认为是校验成功的,即你的回传代码里,ContentType 要这样写:
HttpContext.Response.ContentType = "text/xml; charset=utf-8"
初次写的返回值是JSON格式,坑爹的一直说响应为null,我还以为我的代码响应到宇宙去了。
2.正常签名测试,SDK签名里会根据HttpContext.Request.ContentType的类型走不一样的验证方式
注意:奇门测试环境返回的ContentType为:application/xml,使用SDK验证签名的时候 永远报错:Unspported SPI request。不看SDK源码,你永远想不到原因是何。
///
/// 校验SPI请求签名,不支持带上传文件的HTTP请求。
///
/// HttpRequest对象实例
/// APP密钥
/// 校验结果
public static CheckResult CheckSign(HttpRequest request, string secret)
{
CheckResult result = new CheckResult();
string ctype = request.ContentType;
if (ctype.StartsWith(Constants.CTYPE_APP_JSON) || ctype.StartsWith(Constants.CTYPE_TEXT_XML) || ctype.StartsWith(Constants.CTYPE_TEXT_PLAIN))
{
result.Body = GetStreamAsString(request, GetRequestCharset(ctype));
result.Success = CheckSignInternal(request, result.Body, secret);
}
else if (ctype.StartsWith(Constants.CTYPE_FORM_DATA))
{
result.Success = CheckSignInternal(request, null, secret);
}
else
{
throw new TopException("Unspported SPI request");
}
return result;
}
SDK源码里提到的类型解释:
public const string CTYPE_TEXT_XML = "text/xml";
public const string CTYPE_TEXT_PLAIN = "text/plain";
public const string CTYPE_APP_JSON = "application/json";
public const string CTYPE_FORM_DATA = "application/x-www-form-urlencoded";
压根没提到过 application/xml,你说它能不走Unspported SPI request吗!坑死我了!
没办法我改源码,加一个类型上去
//Constants.CTYPE_APP_PLAIN 为application/xml
if (ctype.StartsWith(Constants.CTYPE_APP_JSON) || ctype.StartsWith(Constants.CTYPE_TEXT_XML) || ctype.StartsWith(Constants.CTYPE_TEXT_PLAIN) || ctype.StartsWith(Constants.CTYPE_APP_PLAIN))
{
result.Body = GetStreamAsString(request, GetRequestCharset(ctype));
result.Success = CheckSignInternal(request, result.Body, secret);
}
好了,到此以为应该没问题了,奈何又报错:Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead
这是读取请求body的时候出错的,不用想了SDK中异常的,一看源码就找到这句话:reader.ReadToEnd()。
这个错是说CORE 3.0中默认禁了AllowSynchronousIO,同步读取body的方式需要ConfigureServices中配置允许同步读取IO流或者直接使用异步读取方式。没用过3.0以前的版本,我们就采用直接使用异步读取方式 方式做的,把代码改成:
ReadToEndAsync().Result。
然后再一次测试,终于签名通过了,其他的就都好说了。
1.签名验证:
这是直连测试,我们对接的第三方也据说都是用的SDK,它们那是对接了好些公司的成熟系统了,理论应该不会出错的吧,并未知晓他们用的何种语言开发的。首次跟他们用奇门对接,就是一直签名通不过,后来查看他们返回的Request.ContentType = "application/x-www-form-urlencoded"。再去查看SDK源码(第一章已经贴出来了),发现生成sign的方法体中不会将解析后的body组装进去。当时我就在想,第三方传过来的签名不会是 走的组装了body生成签名的吧,所以我这里就将Request.ContentType强制修改成"text/xml",然后再去调SDK验证签名方法,结果成功了。
到这里,有2种可能
第一:第三方生成签名不正确,SDK验证方法是对的
第二:SDK生成签名方式与组装请求ContentType 相矛盾,默认都是将body组装生成的签名,但是 请求ContentType 却默认设置了 "application/x-www-form-urlencoded"。
这里我去看了下.net core的版本的SDK,其实发现 它发送请求时候,生成的签名确实是组装了body,但ContentType设置是"text/xml"。按理它不会走偏。但是 第三方说他们是用的SDK组装参数发送请求,不会有错,不知道他们那用的是什么版本的SDK,也没法子验证。后面没法子,就只能在验证签名的之前 强重写了Request.ContentType="text/xml",不然签名验证不过啊。
2. 问题接口:taobao.qimen.returnorder.create(退货单入库推送)
这个是直接跟第三方联调的时候出现的问题,我们按照正常的数据组装发送请求到第三方那里。然后他们那边一直返回参数得不到,解析不了。但是我们的请求跟“发货单创建”接口一样的组装和请求模式,都是用的官方SDK组装参数的,为何 “发货单创建”接口可以成功,“退货单入库”接口就不行呢。这次学聪明了,去看SDK源码,嗯找到问题了。
ReturnorderCreateRequest里的参数,凡是List类型的参数,类名上都没有打上XML解析标志,就是没有下面框中的这句话:
所以没有这句话,它最终生成的报文格式会变成这样,多出的玩意。
ZP ...
加上这句话之后就可以了。
至此,我发现的SDK坑点就这些了,当然这只是我所用到的SDK的相关部分接口发现的坑点而已,还有没有其他地方有,后续用到了在发现吧,至少现在留了心眼,看sdk源码找BUG。
结语:官方称为官方是因为它们一定是经过仔细测试成为了标准唯一。但不可太过依赖官方,不是它们就一定不会出错,凡事留个心眼吧。