在对接api时候,经常需要对字符串进行各种编码处理,系统可能是异构的,实现语言也就可能是不一样的。所以经常会使人犯一些2B的错误!
比如:php实现的api中用了rawurlencode,文档中写,需要对字符串进行urlencode编码,.net的开发人员看到文档,直接使用HttpUtitlity.UrlEncode,
本以为问题已经解决,实际埋下了一个大坑!
其实php的rawurlincode 实现的是RFC3986,.NET(c#)的HttpUtility.UrlEncode实现的并不是RFC3986!所以对同一个字符串编码当然可能产生不同的结果!
关键是这种问题,很难意识到并去解决!所以,以后遇到类似的问题,一定要记得:要面向协议思考、解决问题,否则...
下面附上C#对RFC3986的实现:
/// <summary> /// Provides implementation of RFC3986 Percent Encoding mechanism. /// </summary> public class RFC3986Encoder { /// <summary> /// Performs upper case percent encoding against the specified <paramref name="input"/>. /// </summary> /// <param name="input">The string to encode.</param> /// <returns>Encode string.</returns> public static string Encode(string input) { if (string.IsNullOrEmpty(input)) return input; StringBuilder newStr = new StringBuilder(); foreach (var item in input) { if (IsReverseChar(item)) { newStr.Append("%"); var temp = ((int)item).ToString("X2"); newStr.Append(temp); } else newStr.Append(item); } return newStr.ToString(); } /// <summary> /// Performs lower case percent encoding (url-encodes) on the <paramref name="input"/> as what HttpUtility.UrlEncode does. /// </summary> /// <param name="input">The string to encode.</param> /// <returns>Encode string.</returns> public static string UrlEncode(string input) { StringBuilder newBytes = new StringBuilder(); var urf8Bytes = Encoding.UTF8.GetBytes(input); foreach (var item in urf8Bytes) { if (IsReverseChar((char)item)) { newBytes.Append('%'); newBytes.Append(ByteToHex(item)); } else newBytes.Append((char)item); } return newBytes.ToString(); } private static bool IsReverseChar(char c) { return !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '~'); } private static string ByteToHex(byte b) { return b.ToString("x2"); } private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, 0); /// <summary> /// Gets the seconds from 1970/1/1. /// </summary> /// <param name="time">The time to calculate from.</param> /// <returns>The seconds.</returns> public static int ToUnixTime(DateTime time) { return (int)(time.ToUniversalTime() - UnixEpoch).TotalSeconds; } }