Loading

符合RFC3986规范的编码实现(C#)

在对接第三方平台的api时,有个签名怎么也校验不对,后来发现是其中一步,生成url编码有问题,对方使用的是php中rawurlencode的方法去生成的,而这个方法实现的是RFC3986规范。下面就做下记录。

1.RFC3986规范是什么

RFC3986文档对Url的编解码问题做出了详细的建议,指出了哪些字符需要被编码才不会引起Url语义的转变,以及对为什么这些字符需要编码做出了相应的解释。

1、在US-ASCII字符集中没有的可打印字符:Url中只允许使用可打印字符。US-ASCII码中的10-7F字节全都表示控制字符,这些字符都不能直接出现在Url中。同时,对于80-FF字节(ISO-8859-1),由于已经超出了US-ACII定义的字节范围,因此也不可以放在Url中。

2、保留字符:Url可以划分成若干个组件,协议、主机、路径等。有一些字符(😕?#[]@)是用作分隔不同组件的。例如:冒号用于分隔协议和主机,/用于分隔主机和路径,?用于分隔路径和查询参数,等等。还有一些字符(!$&'()*+,;=)用于在每个组件中起到分隔作用的,如=用于表示查询参数中的键值对,&符号用于分隔查询多个键值对。
RFC3986文档规定,Url中只允许包含以下四种:
1、英文字母(a-zA-Z)
2、数字(0-9)
3、-_.~ 4个特殊字符
4、所有保留字符,RFC3986中指定了以下字符为保留字符(英文字符):! * ' ( ) ; : @ & = + $ , / ? # [ ]

上面的一大段文字都是摘抄的,重要的是最后一句,RFC3986中不能出现诸如! * ' ( ) ; : @ & = + $ , / ? # [ ]此类的字符,所以下面的代码

2.RFC3986Encoder实现

    /// <summary>
    /// 提供对RFC3986协议的编码支持
    /// </summary>
    public class RFC3986Encoder
    {
        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>
        /// 根据RFC3986协议进行UrlEncode,对应php中的rawurlencode
        /// </summary>
        /// <param name="strSrc"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public static string UrlEncode(string strSrc, System.Text.Encoding encoding)
        {
            StringBuilder sb = new StringBuilder();
            var urf8Bytes = encoding.GetBytes(input);
            foreach (var item in urf8Bytes)
            {
                if (IsReverseChar((char)item))
                {
                    sb.Append('%');
                    sb.Append(ByteToHex(item));
                }
                else
                    sb.Append((char)item);
            }
 
            return sb.ToString();
        }

        private static string ByteToHex(byte b)
        {
            return b.ToString("x2");
        }

        private static bool IsReverseChar(char c)
        {
            return !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')
                    || c == '-' || c == '_' || c == '.' || c == '~');
        }
    }
posted @ 2020-11-03 23:33  JolyneStone  阅读(906)  评论(0)    收藏  举报