C#开发BIMFACE系列36 服务端API回调机制
在《C# 开发 BIMFACE 系列文章》中介绍了模型转换、模型对比接口。这2个功能接口比较特殊,发起请求后,逻辑处理是在BIMFACE云端进行的,通常需要5~10分钟。当逻辑处理完成后,BIMFACE通过回调机制通知对比结果。
BIMFACE支持回调机制。在调用方发起模型转换、模型集成、模型对比、生成离线数据包、图纸拆分等接口操作时,可以通过传入参数callback的方式来启用回调机制。 在BIMFACE处理完相应操作后,根据调用方传入的回调地址通知调用方相应操作的结果。
URL参数:
signature(签名):为了确保回调消息是由BIMFACE发出的,调用方在收到回调消息后,须验证签名。签名的计算方式:MD5(``appKey:appSecret:compareId:status:nonce''),如果调用方计算的签名与BIMFACE返回的签名一致,则证明该消息是安全可靠的。
应用收到回调后,须向BIMFace发送回执,回执消息:HTTP STATUS 200
Callbak示例:
* 调用方对文件1685236328506848发起了模型转换,并且传入的回调地址是:https://my.app.com/callback。
* BIMFACE在模型转换任务处理完成后,会发送一个get请求到调用方的callback地址:
https://my.app.com/callback?fileId=1685236328506848&status=success&thumbnail=38044a282f55cb26e3704643dccd2b55/thumbnail/96.png,38044a282f55cb26e3704643dccd2b55/thumbnail/256.png&reason=&signature=99a6fccb1894dfdb4cce48fd5ec58110&nonce=123abc
* 调用方接收到这条请求后,可以进行signature的验证,并发送回执消息。
特别说明
BIMFACE的回调机制与微信公众号或者小程序开发类似,需要开发者提供开发者服务器,且有正式合法域名或者外网IP,对外公布一个地址,BIMFACE服务器能访问到该地址才可以。
如果无法提供有效的回调地址,则只能通过手动调用 模型转换、模型集成、模型对比、生成离线数据包等操作的其他API来获取对应的处理结果。
对于同一个请求的回调,BIMFACE服务器最多回调五次,五次回调后如果还未成功就不再进行回调。
在.NET平台下实现该功能可以使用 WebService、一般处理程序、WebAPI等技术方式实现。下面介绍在一般处理程序中实现的思路与步骤。
1、配置BIMACE开发者账号信息。
在web.config 或者 app.config 文件中配置开发者账号信息,供验证消息签名时使用。
2、获取BIMFace服务器发送的回调请求参数。
1 long fileId = context.Request.QueryString["fileId"].ToLong(); // 文件ID 2 string status = context.Request.QueryString["status"]; // 转换的结果 3 string reason = context.Request.QueryString["reason"]; // 若转换失败,则返回失败原因 4 string thumbnail = context.Request.QueryString["thumbnail"]; // 缩略图地址 5 string nonce = context.Request.QueryString["nonce"]; // 回调随机数 6 string signature = context.Request.QueryString["signature"]; // BIMFACE的加密签名
3、根据请求参数计算签名。
签名的计算方式:MD5(``appKey:appSecret:compareId:status:nonce'')
1 /// <summary> 2 /// 根据回调的参数计算签名 3 /// </summary> 4 /// <param name="appKey">开发者秘钥</param> 5 /// <param name="appSecret">开发者密码</param> 6 /// <param name="fileId">BIMFace发出的回调信息:文件ID</param> 7 /// <param name="status">BIMFace发出的回调信息:转换的结果</param> 8 /// <param name="nonce">BIMFace发出的回调信息:回调随机数</param> 9 /// <returns></returns> 10 public static string GetCallbackSignature(string appKey, string appSecret, long fileId, string status, string nonce) 11 { 12 return string.Format("{0}:{1}:{2}:{3}:{4}", appKey, appSecret, fileId, status, nonce).EncryptByMD5(); 13 }
其中使用到的扩展方法 EncryptByMD5() 实现如下:
1 /// <summary> 2 /// 自定义扩展方法:使用 MD5(不可逆加密) 算法加密字符串。返回二进制形式的字符串。字符串的编码方式为UTF8。 3 /// </summary> 4 /// <param name="this">扩展对象。字符串。编码方式为UTF8</param> 5 /// <param name="caseType">字符串大小写。默认小写</param> 6 /// <returns></returns> 7 public static string EncryptByMD5(this string @this, CaseType caseType = CaseType.Lower) 8 { 9 using (MD5 md5 = MD5.Create()) 10 { 11 var sb = new StringBuilder(); 12 byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(@this)); 13 foreach (byte bytes in hashBytes) 14 { 15 sb.Append(bytes.ToString("X2"));//X2 表示二进制 16 } 17 18 return caseType == CaseType.Upper ? sb.ToString() : sb.ToString().ToLower(); 19 } 20 }
4、验证签名。
将步骤3中的计算结果与BIMFace发出的回调消息签名做对比,如果签名一直则证明该消息是安全可靠的。
1 /// <summary> 2 /// 验证BIMFace发出的回调消息签名信息是否安全可靠 3 /// </summary> 4 /// <param name="appKey">开发者秘钥</param> 5 /// <param name="appSecret">开发者密码</param> 6 /// <param name="fileId">BIMFace发出的回调信息:文件ID</param> 7 /// <param name="status">BIMFace发出的回调信息:转换的结果</param> 8 /// <param name="nonce">BIMFace发出的回调信息:回调随机数</param> 9 /// <param name="signature">BIMFace发出的回调信息:签名</param> 10 /// <param name="custCalcSignature">输出参数:根据BIMFACE平台的加密规则计算出来的签名信息</param> 11 /// <returns></returns> 12 public static bool CheckCallbackSignature(string appKey, string appSecret, long fileId, string status, string nonce, string signature, out string custCalcSignature) 13 { 14 /* signature(签名):为了确保回调消息是由BIMFace发出的,应用在收到回调消息后,须验证签名。 15 * 签名的计算方式:MD5("appKey:appSecret:fileId:status:nonce"),如果应用计算的签名与BIMFace返回的签名一致,则证明该消息是安全可靠的。 16 */ 17 custCalcSignature = GetCallbackSignature(appKey, appSecret, fileId, status, nonce); 18 19 return custCalcSignature == signature; 20 }
5、根据签名验证结果做出回执响应消息。
如果验证签名成功则可以将模型转换、模型集成、模型对比、生成离线数据包等操作的处理结果写入数据库保存供后续其他业务逻辑使用。
签名成功后,须向BIMFace发送回执,回执消息:HTTP STATUS 200。
1 bool checkSignature = CallbackUtils.CheckCallbackSignature(appKey, appSecret, fileId, status, nonce, signature, out custCalcSignature); 2 if (checkSignature) 3 { 4 tip = "[BIMFace发出的回调信息签名验证成功!]" 5 + Environment.NewLine 6 + callbackResponse; 7 LogUtility.Info(tip); 8 9 //Todo 此处可以根据fileId把相关的信息写入数据库中 10 11 // 回执消息:应用收到回调后,须向BIMFace发送回执,回执消息:HTTP STATUS 200 12 context.Response.Write("HTTP STATUS 200"); 13 } 14 else 15 { 16 tip = "[BIMFace发出的回调信息签名验证不通过!]" 17 + Environment.NewLine 18 + callbackResponse 19 + Environment.NewLine 20 + "自定义计算签名 custCalcSignature:" + custCalcSignature; 21 22 LogUtility.Error(tip); 23 24 context.Response.Write(tip); 25 }
如果签名验证失败,则需要将签名信息写入文本日志供分析原因使用。此时通过编码方式实现邮件、短信、微信消息等方式通知开发者回调程序处理结果不正确,使其及时知道业务系统的运行状况。
6、发布程序并使用该回调地址。
程序完成后发布到开发者服务器。在模型转换、模型集成、模型对比、生成离线数据包等操作的API接口参数中使用该回调地址。
完整的代码如下:
1 /// <summary> 2 /// BimFace回调处理 3 /// </summary> 4 public class BimFaceHandler : IHttpHandler 5 { 6 public void ProcessRequest(HttpContext context) 7 { 8 context.Response.ContentType = "text/plain"; 9 context.Response.ContentEncoding = Encoding.UTF8; 10 11 string appKey = ConfigUtility.GetAppSettingValue("BIMFACE_AppKey"); 12 string appSecret = ConfigUtility.GetAppSettingValue("BIMFACE_AppSecret"); 13 string uid = context.Request.QueryString["uid"]; // SparkBimFace 14 15 #region 校验 16 if (appKey.IsNullOrWhiteSpace()) 17 { 18 LogUtility.Error("BIMFace appKey 配置项没有配置!"); 19 20 return; 21 } 22 23 if (appSecret.IsNullOrWhiteSpace()) 24 { 25 LogUtility.Error("BIMFace appSecret 配置项没有配置!"); 26 27 return; 28 } 29 30 if (uid.IsNullOrWhiteSpace()) 31 { 32 LogUtility.Error("[非法请求]回调地址Url链接中的参数 uid 没有配置或者配置的值为空!"); 33 34 return; 35 } 36 #endregion 37 38 long fileId = context.Request.QueryString["fileId"].ToLong(); // 文件ID 39 string status = context.Request.QueryString["status"]; // 转换的结果 40 string reason = context.Request.QueryString["reason"]; // 若转换失败,则返回失败原因 41 string thumbnail = context.Request.QueryString["thumbnail"]; // 缩略图地址 42 string nonce = context.Request.QueryString["nonce"]; // 回调随机数 43 string signature = context.Request.QueryString["signature"]; // BIMFACE的加密签名 44 45 string callbackResponse = string.Format("fileId:{0},\r\nstatus:{1},\r\nreason:{2},\r\nthumbnail:{3},\r\nnonce:{4},\r\nsignature:{5}", 46 fileId, status, reason, thumbnail, nonce, signature); 47 string tip; 48 string custCalcSignature; 49 50 bool checkSignature = CallbackUtils.CheckCallbackSignature(appKey, appSecret, fileId, status, nonce, signature, out custCalcSignature); 51 if (checkSignature) 52 { 53 tip = "[BIMFace发出的回调信息签名验证成功!]" 54 + Environment.NewLine 55 + callbackResponse; 56 LogUtility.Info(tip); 57 58 //Todo 此处可以根据fileId把相关的信息写入数据库中 59 60 // 回执消息:应用收到回调后,须向BIMFace发送回执,回执消息:HTTP STATUS 200 61 context.Response.Write("HTTP STATUS 200"); 62 } 63 else 64 { 65 tip = "[BIMFace发出的回调信息签名验证不通过!]" 66 + Environment.NewLine 67 + callbackResponse 68 + Environment.NewLine 69 + "自定义计算签名 custCalcSignature:" + custCalcSignature; 70 71 LogUtility.Error(tip); 72 73 context.Response.Write(tip); 74 } 75 76 context.Response.End(); 77 } 78 79 /// <summary> 80 /// 该属性获得一个布尔值,指示另一个请求是否可以使用该HTTP处理程序的实例。 81 /// <para>如果设置为true,能提高性能,但要注意线程之间安全性问题。如果设置为false,则线程是安全的</para> 82 /// </summary> 83 public bool IsReusable 84 { 85 get 86 { 87 return false; 88 } 89 } 90 }
BIMFACE服务器端的回调机制非常友好,在BIMFACE服务器与开发者服务器之间自动架起了通讯桥梁。每当BIMFACE服务器上的耗时操作处理结束后(不一定每次都是成功的,也有可能是失败的结果),BIMFACE服务器会及时的将结果推送给开发者服务器,开发者接收到处理结果后进行相应处理即可与业务关联起来。但是使用该回调机制也是有条件限制的,要求开发者服务器以及开发者应用程序是365x24小时运行,中间不能暂停运行,如果遇到以下情况,BIMFACE回调机制将无法推送消息,导致模型的相关状态信息丢失,与开发者的预期不符。
1、开发者服务器网络不稳定。可能导致BIMFACE服务器请求回调地址(开发者服务器)不成功。
2、不定时间的维护开发者服务器、重启服务器等情况。可能导致BIMFACE服务器请求回调地址(开发者服务器)不成功。
3、开发者应用程序不定时间的暂停或停止运行(升级应用程序、维护服务器等原因)。可能导致BIMFACE服务器请求回调地址(开发者服务器)不成功。
4、开发者服务器回调地址中的应用程序处理逻辑不正确。可能导致BIMFACE服务器请求回调地址(开发者服务器)不成功。
5、其他情况···
在开发者发起模型转换、模型集成、模型对比、生成离线数据包等耗时操作的API之后,使用模型之前,需要判断操作结果的状态,然后才能进行其他的业务操作。比如发起模型转换这个动作,开发者调用模型转换API之后,BIMFACE服务器开始对模型进行转换,但是转换需要一个过程,并不能立刻转换完成,至少需要等待几秒。如果此时开发者想立刻预览该模型,那么第一步就需要判断模型是否已经转换成功,因为只有转换成功的模型才能预览。所以开发者第一次就需要手动调用【获取转换状态】接口来查询该模型的转换状态,转换状态有三种值:processing、 success、failed。
(1)如果【获取转换状态】接口返回结果是 processing,则开发者需要在UI端以友好的提示告知当前操作者:模型正在转换中,暂时无法预览。等待转换完成后再预览。
(2)如果【获取转换状态】接口返回结果是 failed,则开发者需要在UI端以友好的提示告知当前操作者:模型转换失败,暂时无法预览。开发者需要去排查转换失败的原因并解决。转换失败有可能是图纸的问题,或者网络的问题等。
(3)如果【获取转换状态】接口返回结果是 success,则应用程序直接跳转到预览界面即可进行查看。
以上三种状态,无论返回哪种状态,都需要把状态值保存到数据库中。供后续使用。
第二次及以后操作(预览等),开发者需要到数据库中查找该模型的状态,如果是 processing 或者 failed, 则需要开发者再次调用【获取转换状态】接口,并将获取到的状态值更新到数据库中。重复该操作,直到查询返回的状态是 success 才可以进行后续的操作。
总结:不建议使用BIMFACE服务器端API的回调机制。
最稳妥的方式就是直接调用各个耗时操作对应的查询操作结果的接口。
成在管理,败在经验;嬴在选择,输在不学! 贵在坚持!
个人作品
BIMFace.SDK.NET
开源地址:https://gitee.com/NAlps/BIMFace.SDK
系列博客:https://www.cnblogs.com/SavionZhang/p/11424431.html
系列视频:https://www.cnblogs.com/SavionZhang/p/14258393.html
技术栈
1、Visual Studio、.NET Core/.NET、MVC、Web API、RESTful API、gRPC、SignalR、Java、Python
2、jQuery、Vue.js、Bootstrap、ElementUI
3、数据库:分库分表、读写分离、SQLServer、MySQL、PostgreSQL、Redis、MongoDB、ElasticSearch、达梦DM
4、架构:DDD、ABP、SpringBoot、jFinal
5、环境:跨平台、Windows、Linux、Nginx
6、移动App:Android、IOS、HarmonyOS、微信小程序、钉钉、uni-app、MAUI
分布式、高并发、云原生、微服务、Docker、CI/CD、DevOps、K8S;Dapr、RabbitMQ、Kafka、RPC、Elasticsearch。
欢迎关注作者头条号 张传宁IT讲堂,获取更多IT文章、视频等优质内容。
出处:www.cnblogs.com/SavionZhang
作者:张传宁 技术顾问、培训讲师、微软MCP、系统架构设计师、系统集成项目管理工程师、科技部创新工程师。
专注于企业级通用开发平台、工作流引擎、自动化项目(代码)生成器、SOA 、DDD、 云原生(Docker、微服务、DevOps、CI/CD);PDF、CAD、BIM 审图等研究与应用。
多次参与电子政务、图书教育、生产制造等企业级大型项目研发与管理工作。
熟悉中小企业软件开发过程:可行调研、需求分析、架构设计、编码测试、实施部署、项目管理。通过技术与管理帮助中小企业实现互联网转型升级全流程解决方案。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
如有问题,可以通过邮件905442693@qq.com联系。共同交流、互相学习。
如果您觉得文章对您有帮助,请点击文章右下角【推荐】。您的鼓励是作者持续创作的最大动力!