C#对接----韵达开发平台--取电子面单
引子
最近根据业务的一些需求,所以放弃从快递鸟对接去电子面单,转而直接对接韵达开发平台:http://open.yundasys.com/ ,中间踩了一些坑,借此做了一个小案例给大伙,瞅瞅,若有需改进之处,还请指出!!!
废话不多数:首先咱先对韵达的一些接口参数了解清楚:
当然附上地址:http://open.yundasys.com/index.php?g=&m=ApiTools&a=exm
还有接口的一些SDK文件地址,这个就各位观众大老爷们自己去看了:http://open.yundasys.com/index.php?g=&m=ApiTools&a=apps&id=14
解决方案
上代码走起:基础参数的模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 {<br> //请求参数 class RequestVO { /// <summary> /// XML数据内容 /// </summary> public string xmldata { get ; set ; } /// <summary> /// 合作社区ID,由韵达给大客户提供 /// </summary> public string partnerid { get ; set ; } /// <summary> /// 密码 /// </summary> public string password { get ; set ; } /// <summary> /// 数据请求类型,如request=data;其中data表示下单,详细请见request字典表 /// </summary> public string request { get ; set ; } /// <summary> /// 请求的版本,当前版本为1.0 /// </summary> public string version { get ; set ; } } } |
主体参数模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml.Serialization; namespace ConsoleApplication2 { /// <summary> /// 数据体 /// </summary> public class Orders { [XmlElement( "order" )] public List<Order> order { get ; set ; } } /// <summary> /// 韵达取号订单信息 /// </summary> public class Order { /// <summary> /// 订单唯一序列号 /// </summary> public string order_serial_no { get ; set ; } /// <summary> /// 大客户系统订单的订单号 /// </summary> public string khddh { get ; set ; } /// <summary> /// 内部参考号,供大客户自己使用,可以是客户的客户编号 /// </summary> public string nbckh { get ; set ; } /// <summary> /// 单号 /// </summary> public string mailno { get ; set ; } /// <summary> /// 发件人 /// </summary> [XmlElement( "sender" )] public Sender sender { get ; set ; } /// <summary> /// 收件人 /// </summary> [XmlElement( "receiver" )] public Receiver receiver { get ; set ; } /// <summary> /// 物品重量 /// </summary> public long weight { get ; set ; } /// <summary> /// 尺寸,格式(长,宽,高),单位cm /// </summary> public string size { get ; set ; } /// <summary> /// 货物金额 /// </summary> public decimal value { get ; set ; } /// <summary> /// 商品集合 /// </summary> [XmlElement( "items" )] public Items items { get ; set ; } /// <summary> /// 订单备注 /// </summary> public string remark { get ; set ; } /// <summary> /// 可以自定义显示信息1 /// </summary> public string cus_area1 { get ; set ; } /// <summary> /// 可以自定义显示信息2 /// </summary> public string cus_area2 { get ; set ; } } public class Sender { /// <summary> /// 姓名 /// </summary> public string name { get ; set ; } /// <summary> /// 公司 /// </summary> public string company { get ; set ; } /// <summary> /// 严格按照国家行政区划,省市区三级,逗号分隔。示例上海市,上海市,青浦区(cod订单必填) /// </summary> public string city { get ; set ; } /// <summary> /// 需要将省市区划信息加上,例如:上海市,上海市,青浦区盈港东路7766号 /// </summary> public string address { get ; set ; } /// <summary> /// 邮编 /// </summary> public string postcode { get ; set ; } /// <summary> /// 固定电话 /// </summary> public string phone { get ; set ; } /// <summary> /// 移动电话固定电话或移动电话至少填一项 /// </summary> public string mobile { get ; set ; } public string branch { get ; set ; } } public class Receiver { /// <summary> /// 姓名 /// </summary> public string name { get ; set ; } /// <summary> /// 公司 /// </summary> public string company { get ; set ; } /// <summary> /// 严格按照国家行政区划,省市区三级,逗号分隔。示例上海市,上海市,青浦区(cod订单必填) /// </summary> public string city { get ; set ; } /// <summary> /// 需要将省市区划信息加上,例如:上海市,上海市,青浦区盈港东路7766号 /// </summary> public string address { get ; set ; } /// <summary> /// 邮编 /// </summary> public string postcode { get ; set ; } /// <summary> /// 固定电话 /// </summary> public string phone { get ; set ; } /// <summary> /// 移动电话固定电话或移动电话至少填一项 /// </summary> public string mobile { get ; set ; } public string branch { get ; set ; } } /// <summary> /// 明细集合 /// </summary> public class Items { [XmlElement( "item" )] public List<Item> item { get ; set ; } } /// <summary> /// 明细信息 /// </summary> public class Item { /// <summary> /// 商品名称 /// </summary> public string name { get ; set ; } /// <summary> /// 商品数量 /// </summary> public int number { get ; set ; } /// <summary> /// 商品备注 /// </summary> public string remark { get ; set ; } } } |
请求方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using System.IO; using System.Collections.Specialized; using System.Net; namespace ConsoleApplication1 { /// <summary> /// POST提交 /// </summary> class HttpClient { /// <summary> /// /// </summary> /// <param name="url"></param> /// <param name="parameters"></param> /// <returns></returns> public static HttpWebResponse post(String url, IDictionary< string , string > parameters) { HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; request.Method = "POST" ; request.ContentType = "application/x-www-form-urlencoded" ; //如果需要POST数据 if (!(parameters == null || parameters.Count == 0)) { StringBuilder buffer = new StringBuilder(); int i = 0; foreach ( string key in parameters.Keys) { if (i > 0) { buffer.AppendFormat( "&{0}={1}" , key, parameters[key]); } else { buffer.AppendFormat( "{0}={1}" , key, parameters[key]); } i++; } byte [] data = Encoding.UTF8.GetBytes(buffer.ToString()); using (Stream stream = request.GetRequestStream()) { stream.Write(data, 0, data.Length); } } return request.GetResponse() as HttpWebResponse; } /// <summary> /// /// </summary> /// <param name="url"></param> /// <param name="data"></param> /// <returns></returns> public static String post(String url, String postdata) { try { HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; request.Method = "POST" ; request.ContentType = "application/x-www-form-urlencoded" ; byte [] data = Encoding.UTF8.GetBytes(postdata.ToString()); using (Stream stream = request.GetRequestStream()) { stream.Write(data, 0, data.Length); } HttpWebResponse response = request.GetResponse() as HttpWebResponse; StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); string outMessage = sr.ReadToEnd(); sr.Close(); return outMessage; } catch (Exception ex) { throw ex; } } } } |
要求
当前按照SDK的要求:
请求报文说明:
1. 数据传输以HTTP POST方式发送,数据字符集一律采用UTF-8
2. xmldata首先需要进行base64编码
3. validation的效验方式采用 MD5(xmldata + partnerid + 密码),这里的加号为字符串连接符号。
4. 所有参数最终均须在完成数据转换后进行URL编码。
请求报文详细解释:
1.假设partnerid为YUNDA;密码为123456;xmldata内容为
<order></order>
2.xmldata经过base64编码以后变成PG9yZGVyPjwvb3JkZXI+
3.那么要签名的内容为PG9yZGVyPjwvb3JkZXI+YUNDA123456,经过md5后的内容就为f197e870a12528e38cb483b4e371f4ea
4.然后再对xmldata经过URL编码,得到字符串PG9yZGVyPjwvb3JkZXI%2B
5.同样需要对其他字段进行URL编码,否则可能会影响POST传递,具体请参见HTTP POST传输协议
6.最终要发送的数据为: partnerid=YUNDA&version=1.0&request=data&xmldata=PG9yZGVyPjwvb3JkZXI%2B&validation=f197e870a12528e38cb483b4e371f4ea
不拉不拉不拉,一大堆,大老爷们自己去看,这些数据转换的方法我直接贴出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | using System; using System.Text; using System.Web; using System.IO; using System.Xml.Serialization; namespace ConsoleApplication1 { class DataTransform { /// <summary> /// 组装主体内容 /// </summary> /// <param name="requestVO"></param> /// <returns></returns> public static String signData(RequestVO requestVO) { String xmldata = Convert.ToBase64String(System.Text.Encoding.GetEncoding( "UTF-8" ).GetBytes(requestVO.xmldata)); string validation = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(xmldata + requestVO.partnerid + requestVO.password, "MD5" ).ToLower(); string signdata = "partnerid=" + requestVO.partnerid + "&version=" + requestVO.version + "&request=" + requestVO.request + "&xmldata=" + HttpUtility.UrlEncode(xmldata) + "&validation=" + validation; return signdata; } /// <summary> /// 内容数据转换XML /// </summary> /// <param name="type"></param> /// <param name="obj"></param> /// <returns></returns> public static String obj2Xml(Type type, Object obj) { XmlSerializer xml = new XmlSerializer(type); String xmldata = "" ; using (MemoryStream stream = new MemoryStream()) { try { xml.Serialize(stream, obj); xmldata = Encoding.UTF8.GetString(stream.GetBuffer(), 0, ( int )stream.Length); } catch (Exception) { throw ; } } return xmldata; } /// <summary> /// 内容清洗转换 /// </summary> /// <param name="xml"></param> /// <returns></returns> public static string xmlformat( string xml) { try { System.Xml.XmlDocument doc = new System.Xml.XmlDocument(); doc.LoadXml(xml); System.IO.StringWriter sw = new System.IO.StringWriter(); using (System.Xml.XmlTextWriter writer = new System.Xml.XmlTextWriter(sw)) { writer.Indentation = 2; // the Indentation writer.Formatting = System.Xml.Formatting.Indented; doc.WriteContentTo(writer); writer.Close(); } return sw.ToString(); } catch (Exception ex) { return xml; } } } } |
哈哈看了这么多了 咱还没看到请求电子面单的方法是吧 别急
这个类是我自己整合的在项目里的,大老爷们先看看有不足之处 指点指点,应该能看明白!哈哈!案例的是winfrom,这个类没有用上,方法我就不贴出来了,大佬自己去最底下载吧!!!
using Commons.BLL; using Commons.Model; using Commons.Settings; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using System.Web; using System.Xml; using System.Xml.Serialization; namespace Commons.Helpers { public class YunDaApiHelper { SettingService _settingService=new SettingService(); /// <summary> /// 韵达电子面单请求url /// </summary> private static string _createYunDaUrl; /// <summary> /// 取消韵达电子面单url /// </summary> private static string _colseYunDaUrl; //韵达ID,密码 private static string _partnerid; private static string _password; //发件人信息 public static string FHCompany; public static string FHName; public static string FHMobile; public static string FHProvinceName; public static string FHCityName; public static string FHExpAreaName; public static string FHAddress; public YunDaApiHelper() {//_createYunDaUrl = "http://orderdev.yundasys.com:10110/cus_order/order_interface/interface_receive_order__mailno.php";//测试 //_colseYunDaUrl = "http://orderdev.yundasys.com:10110/cus_order/order_interface/interface_cancel_order.php";//测试 var settings = _settingService.LoadSetting<KdniaoSettings>(); _createYunDaUrl = settings.CreateYunDaUrl; _colseYunDaUrl = settings.ColseYunDaUrl; FHCompany = settings.FHCompany; FHName = settings.FHName; FHMobile = settings.FHMobile; FHProvinceName = settings.FHProvinceName; FHCityName = settings.FHCityName; FHExpAreaName = settings.FHExpAreaName; FHAddress = settings.FHAddress; _partnerid = settings.YdPartnerId; _password = settings.YdPassword; } /// <summary> /// 申请韵达电子面单 /// </summary> /// <param name="order"></param> /// <returns></returns> public YunDaResult CreateYunDaNo(Order order) { var model = new YDOrderModel(); var send = new YDSender { name = FHName,//发货人名称 company = FHCompany,//发货人公司 mobile = FHMobile,//发货人移动电话或手机 address = FHAddress,//发货人地址,需要将省市区划信息加上,例如:上海市,上海市,青浦区盈港东路7766号 postcode = "510000", //邮编 city = FHProvinceName + FHCityName + FHExpAreaName,//严格按照国家行政区划,省市区三级,逗号分隔。示例上海市,上海市,青浦区(cod订单必填) phone = "",//固话 branch = "" }; order.sender = send; model.order = order; try { var xml = Obj2Xml(typeof (YDOrderModel), model); var requestVo = new YunDaRequestModel { xmldata = xml, partnerid = _partnerid, password = _password, version = "1.0", request = "data" }; var data = SignData(requestVo); var result = Post(_createYunDaUrl, data); var msgBody = new XmlDocument(); msgBody.LoadXml(result); var status = GetXmlValue(msgBody, "status"); var dto = new YunDaResult { status = Convert.ToInt32(status), order_serial_no = GetXmlValue(msgBody, "order_serial_no"), msg = GetXmlValue(msgBody, "msg"), mail_no = GetXmlValue(msgBody, "mail_no") }; return dto; } catch (Exception ex) { var dto = new YunDaResult { status = (int) CustomBoolean.False, msg = ex.ToString() }; return dto; } } /// <summary> /// 取消韵达电子面单 /// </summary> /// <param name="xml"></param> /// <returns></returns> public YunDaResult ColseYunDaNo(string xml) { var requestVo = new YunDaRequestModel { xmldata = Xmlformat(xml), partnerid = _partnerid, password = _password, version = "1.0", request = "cancel_order" }; try { var data = SignData(requestVo); var result = Post(_colseYunDaUrl, data); var msgBody = new XmlDocument(); msgBody.LoadXml(result); var dto = new YunDaResult { status = Convert.ToInt32(GetXmlValue(msgBody, "status")), order_serial_no = GetXmlValue(msgBody, "order_serial_no"), msg = GetXmlValue(msgBody, "msg") }; return dto; } catch (Exception ex) { var dto = new YunDaResult { status = (int) CustomBoolean.False, msg = ex.ToString() }; return dto; } } #region 组装数据以及转化xml数据 /// <summary> /// 组装主体内容 /// </summary> /// <param name="requestVo"></param> /// <returns></returns> public static string SignData(YunDaRequestModel requestVo) { var xmldata = Convert.ToBase64String(System.Text.Encoding.GetEncoding("UTF-8").GetBytes(requestVo.xmldata)); var validation = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(xmldata + requestVo.partnerid + requestVo.password, "MD5").ToLower(); var signdata = "partnerid=" + requestVo.partnerid + "&version=" + requestVo.version + "&request=" + requestVo.request + "&xmldata=" + HttpUtility.UrlEncode(xmldata) + "&validation=" + validation; return signdata; } /// <summary> /// 内容数据转换XML /// </summary> /// <param name="type"></param> /// <param name="obj"></param> /// <returns></returns> public static string Obj2Xml(Type type, object obj) { var xml = new XmlSerializer(type); var xmldata = ""; using (var stream = new MemoryStream()) { try { xml.Serialize(stream, obj); xmldata = Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int)stream.Length); } catch (Exception) { throw; } } return xmldata; } /// <summary> /// 内容清洗转换 /// </summary> /// <param name="xml"></param> /// <returns></returns> public static string Xmlformat(string xml) { try { var doc = new System.Xml.XmlDocument(); doc.LoadXml(xml); var sw = new System.IO.StringWriter(); using (var writer = new System.Xml.XmlTextWriter(sw)) { writer.Indentation = 2; // the Indentation writer.Formatting = System.Xml.Formatting.Indented; doc.WriteContentTo(writer); writer.Close(); } return sw.ToString(); } catch (Exception ex) { return xml; } } #endregion #region Post数据请求 public static HttpWebResponse Post(string url, IDictionary<string, string> parameters) { var request = WebRequest.Create(url) as HttpWebRequest; request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; //如果需要POST数据 if (!(parameters == null || parameters.Count == 0)) { var buffer = new StringBuilder(); var i = 0; foreach (var key in parameters.Keys) { buffer.AppendFormat(i > 0 ? "&{0}={1}" : "{0}={1}", key, parameters[key]); i++; } var data = Encoding.UTF8.GetBytes(buffer.ToString()); using (var stream = request.GetRequestStream()) { stream.Write(data, 0, data.Length); } } return request.GetResponse() as HttpWebResponse; } public static string Post(string url, string postdata) { try { var request = WebRequest.Create(url) as HttpWebRequest; request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; var data = Encoding.UTF8.GetBytes(postdata.ToString()); using (Stream stream = request.GetRequestStream()) { stream.Write(data, 0, data.Length); } var response = request.GetResponse() as HttpWebResponse; var sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); var outMessage = sr.ReadToEnd(); sr.Close(); return outMessage; } catch (Exception ex) { throw ex; } } #endregion #region MyRegion /// <summary> /// XML读取对应的值 /// </summary> /// <param name="msgBody">xml</param> /// <param name="nodeName">节点名称</param> /// <returns>返回节点值</returns> public static string GetXmlValue(XmlDocument msgBody, string nodeName) { var fromUserName = msgBody.GetElementsByTagName(nodeName).Item(0); return fromUserName?.InnerText; } #endregion } }
SettingService 这个是系统配置参数,应该没毛病哈哈!
so,下边咱来看看案例的界面

注
账号:韵达的客户号
密码:是韵达二维码VIP客户端的《接口联调密码》
结语
案例很简单,但是有包含蛮多东东的,各位大佬只要是搞通一个,那估摸着就都没问题了!
链接:https://pan.baidu.com/s/1T3X8-TLorn5R8nZfpKkqOg 密码:m645 ------地址要是挂了,各位直接联系我哈!
好了!各位大老爷觉着这篇文章要是不错就点个赞咯
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!


您的资助是我最大的动力!
金额随意,欢迎来赏!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构