WebApi接口开发
文档 规范的文档对接口的开发有着至关重要的作用,规范的文档能够使 双方对接口的定义以及接口的参数都有一个明确的概念,便于沟通和联调。
在接口的开发过程中,为了保证传递参数的传递的私密性,参数传输是需要加签的,接口提供方提供了一种验签规则,接口的调用方提供合适的参数后通过加签的操作以参数的形式传输给接口的提供方
下面提供一种验签的思路
step1: 拼接参数字符串将除sign以外的请求参数(包括标准参数,除非有特别说明)按照参数名称的字典升序排列,然后按此顺序,将”参数名=参数值”用”&”符号连接,结果形如:”参数名1=参数值1&参数名2=参数值2&…&参数名n=参数值n”。
注意事项:
1) 参数值应为urlencode过后的字符串。
2) 仅对接口定义中声明且请求参数列表中包含的参数(包括空值)进行签名。
3) 参数值不作去除空格。
step2: 计算参数字符串&appSecret的hash摘要digest=md5(参数字符串&appSecret)
step3: 将二进制的摘要转换为16进制表示sign=toHex(digest),注:签名比较无需区分大小写
/// <summary> /// 验证该商家数据和签名 /// </summary> /// <param name="requestData"></param> /// <returns></returns> private bool CheckSignValidate(ParameterWrapper requestData) { string method = requestData[SystemMethodParameterNames.method]; string appid = requestData[SystemMethodParameterNames.app_id]; string appSecret = string.Empty; List<RouteConfig> routeList = RouteConfigManager.GetRouteConfig(); var routeConfigInfo = routeList.First(f => f.Name == method.Trim().ToLower()); if (appid.ToLower() == ConfigurationManager.AppSettings["DataExchangeCenter_AppID"].ToLower()) { //账册中间件: if (routeConfigInfo.CallerType != 0) { throw new SysException(new KeyValuePair<string, string>("-4", "用户不支持调用当前API接口!")); } appSecret = ConfigurationManager.AppSettings["DataExchangeCenter_AppSecret"]; } else { //其他系统/商家: if (routeConfigInfo.CallerType != 1) { throw new SysException(new KeyValuePair<string, string>("-4", "用户不支持调用当前API接口!")); } appSecret = AppSecretKeyDA.GetAppSecretKeyByKJTAppID(appid); if (string.IsNullOrEmpty(appSecret)) { throw new SysException(SysExceptionContent.getInvalidIdentityId()); } } SellerAPIParameter sellerParam = new SellerAPIParameter() { SecretKey = CryptoManager.GetCrypto(CryptoAlgorithm.DES).Encrypt(appSecret) }; if (sellerParam == null) { throw new SysException(SysExceptionContent.getNoFoundSeller()); } if (string.IsNullOrEmpty(sellerParam.SecretKey)) { throw new SysException(SysExceptionContent.getSecureKeyNullOrEmpty()); }//对数据库中取出的SecretKey进行解密 sellerParam.SecretKey = CryptoManager.GetCrypto(CryptoAlgorithm.DES).Decrypt(sellerParam.SecretKey); SignatureType signatureType = SignatureType.MD5; SignatureBuilder builder = new SignatureBuilder(sellerParam.SecretKey, signatureType, requestData); string sign = builder.BuildSignature(); string requestSign = requestData[SystemMethodParameterNames.sign].ToLower(); return requestSign == sign; }
上面是接口的提供方验签的方法,思路的实现就是上面的部分,具体签名的生成实在SignatureBuilder 这个类中实现
测试的时候模拟了接口调用方,用的是一个Winform的简单窗体
private void button1_Click(object sender, EventArgs e) { System.Text.UTF8Encoding encoding = new UTF8Encoding(); StringBuilder sb = new StringBuilder(); sb.Append("method=CreatePO&"); sb.Append("version=1.0&"); sb.Append("appid=32&"); sb.Append("data="); sb.Append("{\"MerchantSysNo\":32,\"WarehouseID\":51,\"CallBackUrl\":\"http://localhost:9998/open.api\",\"ETATime\":\"2015-03-20\",\"HalfDay\":\"PM\",\"Items\":[{\"MerchantProductID\":\"5900820010218\",\"Qty\":10},{\"MerchantProductID\":\"4902508040327\",\"Qty\":20}]}"); string requestBody = sb.ToString(); NameValueCollection nv = new NameValueCollection(); if (requestBody.Contains('&')) { foreach (var item in requestBody.Split('&')) { nv.Add(item.Split('=')[0], item.Split('=')[1]); } } else { nv.Add(requestBody.Split('=')[0], requestBody.Split('=')[1]); } ParameterWrapper parameters = GetParameterWrapperFromNameValueCollection(nv); SignatureType signatureType = SignatureType.MD5; SignatureBuilder builder = new SignatureBuilder("987654321", signatureType, parameters); string sign = builder.BuildSignature(); sb.Append("&sign=").Append(sign); byte[] rdata = encoding.GetBytes(sb.ToString()); HttpWebRequest reqclient = WebRequest.Create("http://localhost:9998/open.api") as HttpWebRequest; reqclient.Method = "POST"; reqclient.ContentType = "application/x-www-form-urlencoded"; reqclient.ContentLength = rdata.Length; Stream newStream = reqclient.GetRequestStream(); newStream.Write(rdata, 0, rdata.Length); newStream.Close(); // Get response HttpWebResponse myResponse = (HttpWebResponse)reqclient.GetResponse(); StreamReader reader = new StreamReader(myResponse.GetResponseStream(), Encoding.UTF8); string content = reader.ReadToEnd(); label1.Text = content; } private ParameterWrapper GetParameterWrapperFromNameValueCollection(NameValueCollection nv) { ParameterWrapper parameter = new ParameterWrapper(); foreach (string key in nv.AllKeys) { string value = nv[key]; value = HttpUtility.UrlDecode(nv[key], Encoding.UTF8); parameter.Add(key, value); } return parameter; }
这样就实现了接口的开发以及调用的过程,当然这只是接口开发中的一部分,剩下的内容大部分都是和之前的模块开发没有什么太大的区别,实现数据的处理。