引子
最近根据业务的一些需求,所以放弃从快递鸟对接去电子面单,转而直接对接韵达开发平台: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
解决方案
上代码走起:基础参数的模型
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 {
//请求参数 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; } } }
主体参数模型
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; } } }
请求方法:
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
不拉不拉不拉,一大堆,大老爷们自己去看,这些数据转换的方法我直接贴出:
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 ------地址要是挂了,各位直接联系我哈!
好了!各位大老爷觉着这篇文章要是不错就点个赞咯