Uri.EscapeDataString() - 无效的 URI:Uri 字符串太长

Posted

技术标签:

【中文标题】Uri.EscapeDataString() - 无效的 URI:Uri 字符串太长【英文标题】:Uri.EscapeDataString() - Invalid URI: The Uri string is too long 【发布时间】:2011-10-05 10:19:29 【问题描述】:

我在 windows mobile 上使用紧凑型框架/C#。

在我的应用程序中,我通过序列化对象并使用 HttpWebRequest/POST 请求将信息向上发送来将数据上传到服务器。在服务器上,发布数据被反序列化并保存到数据库中。

前几天我意识到我在帖子数据中遇到了特殊字符(和号等)的问题。所以我在方法中引入了 Uri.EscapeDataString() ,一切都很好。

然而,今天我发现当应用程序尝试上传大量数据时出现问题(我不确定究竟什么是“大”!)

现有代码(种类)

var uploadData = new List<Things>();

uploadData.Add(new Thing()  Name = "Test 01" );
uploadData.Add(new Thing()  Name = "Test 02" );
uploadData.Add(new Thing()  Name = "Test with an & Ampersand " ); // Do this a lot!!

var postData = "uploadData=" + Uri.EscapeDataString(JsonConvert.SerializeObject(uploadData, new IsoDateTimeConverter()));

问题

对 Uri.EscapeDataString() 的调用导致以下异常:

System.UriFormatException: Invalid URI: Uri 字符串太长。

问题

还有其他方法可以准备要上传的数据吗?

据我所知,HttpUtility(它有自己的编码/解码方法)不适用于紧凑型框架。

【问题讨论】:

你可以编写你自己的实现吗? EscapeDataString() 似乎主要是方便...根据需要转义的字符库做一个普通的String.Replace Msdn 声明:UriFormatException - stringToEscape 的长度超过 32766 个字符。 按照 Smudge202 的建议,我只是编写了自己的实现。 发布这个实现怎么样? 我会发布实现,但它有点结痂!!我最近改用接受的答案。 【参考方案1】:

或者您可以简单地拆分字符串并为每个块调用Uri.EscapeDataString(string),以避免重新实现函数。

示例代码:

        String value = "large string to encode";
        int limit = 2000;

        StringBuilder sb = new StringBuilder();
        int loops = value.Length / limit;

        for (int i = 0; i <= loops; i++)
        
            if (i < loops)
            
                sb.Append(Uri.EscapeDataString(value.Substring(limit * i, limit)));
            
            else
            
                sb.Append(Uri.EscapeDataString(value.Substring(limit * i)));
            
        

【讨论】:

.net 4.5 中 EscapeDataString 的限制为 65520 个字符 - 因此可用于减少所需的迭代次数。 酷。 Uri.Unescape 有这种问题吗?似乎不是,但我想以防万一 @Knagis 我不确定你为什么在这里提到迭代次数,因为这几乎不可能占执行时间的很大一部分。用 value.Length 的大小初始化 StringBuilder 听起来肯定会带来更好的性能提升。 只是更新:.NET 4.5 中 EscapeDataString 的正确当前限制是 32766 个字符(不是上面@Knagi 提到的 65520):msdn.microsoft.com/en-us/library/… @Nick 如果你真的尝试过,你可能会发现 65520 是实际的限制(不包括,所以 65519 是最有效的),尽管文档说了什么。【参考方案2】:

“Alberto de Paola”的答案很好。

尽管如此,对转义的数据进行转义有点棘手,因为您必须避免在编码字符的中间切割编码字符串(否则会破坏原始字符串的完整性)。

这是我解决此问题的方法:

public static string EncodeString(string str)

    //maxLengthAllowed .NET < 4.5 = 32765;
    //maxLengthAllowed .NET >= 4.5 = 65519;
    int maxLengthAllowed = 65519;
    StringBuilder sb = new StringBuilder();
    int loops = str.Length / maxLengthAllowed;

    for (int i = 0; i <= loops; i++)
    
        sb.Append(Uri.EscapeDataString(i < loops
            ? str.Substring(maxLengthAllowed * i, maxLengthAllowed)
            : str.Substring(maxLengthAllowed * i)));
    

    return sb.ToString();


public static string DecodeString(string encodedString)

    //maxLengthAllowed .NET < 4.5 = 32765;
    //maxLengthAllowed .NET >= 4.5 = 65519;
    int maxLengthAllowed = 65519;

    int charsProcessed = 0;
    StringBuilder sb = new StringBuilder();

    while (encodedString.Length > charsProcessed)
    
        var stringToUnescape = encodedString.Substring(charsProcessed).Length > maxLengthAllowed
            ? encodedString.Substring(charsProcessed, maxLengthAllowed)
            : encodedString.Substring(charsProcessed);

        // If the loop cut an encoded tag (%xx), we cut before the encoded char to not loose the entire char for decoding
        var incorrectStrPos = stringToUnescape.Length == maxLengthAllowed ? stringToUnescape.IndexOf("%", stringToUnescape.Length - 4, StringComparison.InvariantCulture) : -1;
        if (incorrectStrPos > -1)
        
            stringToUnescape = encodedString.Substring(charsProcessed).Length > incorrectStrPos
                ? encodedString.Substring(charsProcessed, incorrectStrPos)
                : encodedString.Substring(charsProcessed);
        

        sb.Append(Uri.UnescapeDataString(stringToUnescape));
        charsProcessed += stringToUnescape.Length;
    

    var decodedString = sb.ToString();

    // ensure the string is sanitized here or throw exception if XSS / SQL Injection is found
    SQLHelper.SecureString(decodedString);
    return decodedString;

测试这些功能:

var testString = "long string to encode";
var encodedString = EncodeString(testString);
var decodedString = DecodeString(encodedString);

Console.WriteLine(decodedString == testString ? "integrity respected" : "integrity broken");

希望这可以帮助避免一些头痛;)

【讨论】:

这构建了一个更好的整体解决方案。我被要翻译的字符中间的裂口咬住了。【参考方案3】:
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < originalString.Length; i++)

    if ((originalString[i] >= 'a' && originalString[i] <= 'z') || 
        (originalString[i] >= 'A' && originalString[i] <= 'Z') || 
        (originalString[i] >= '0' && originalString[i] <= '9'))
    
        stringBuilder.Append(originalString[i]);
    
    else
    
        stringBuilder.AppendFormat("%0:X2", (int)originalString[i]);
    


string result = stringBuilder.ToString();

【讨论】:

【参考方案4】:

我一直在使用 System.Web.HttpUtility.UrlEncode,似乎可以更好地处理较长的字符串。

【讨论】:

【参考方案5】:

使用System.Web.HttpUtility.UrlEncode(基于this answer):

        value = HttpUtility.UrlEncode(value)
            .Replace("!", "%21")
            .Replace("(", "%28")
            .Replace(")", "%29")
            .Replace("*", "%2A")
            .Replace("%7E", "~"); // undo escape

【讨论】:

百分比呢? 链接的答案包括:WebUtility.UrlEncode将空间编码为+Uri.EscapeDataString 将其编码为 %20。我们不应该因此添加.Replace("+", "%20")吗?【参考方案6】:

我需要另一个解决方案,因为 Pouki 的解决方案在处理 Cyrillic 并剪切符号时不起作用。

替代方案如下:

    protected const int MaxLengthAllowed = 32765;
    private static string UnescapeString(string encodedString)
    
        var charsProccessed = 0;

        var sb = new StringBuilder();

        while (encodedString.Length > charsProccessed)
        
            var isLastIteration = encodedString.Substring(charsProccessed).Length < MaxLengthAllowed;

            var stringToUnescape = isLastIteration
                ? encodedString.Substring(charsProccessed)
                : encodedString.Substring(charsProccessed, MaxLengthAllowed);

            while (!Uri.IsWellFormedUriString(stringToUnescape, UriKind.RelativeOrAbsolute) || stringToUnescape.Length == 0)
            
                stringToUnescape = stringToUnescape.Substring(0, stringToUnescape.Length - 1);
            

            sb.Append(Uri.UnescapeDataString(stringToUnescape));
            charsProccessed += stringToUnescape.Length;
        

        return sb.ToString();
    

【讨论】:

以上是关于Uri.EscapeDataString() - 无效的 URI:Uri 字符串太长的主要内容,如果未能解决你的问题,请参考以下文章

如何让 Uri.EscapeDataString 符合 RFC 3986

通过“HttpUtility.UrlEncode”和“Uri.EscapeDataString”在 GET API 请求中编码撇号会出错

EscapeUriString 和 EscapeDataString 有啥区别?

如何在 WinForms 中对 URL 进行编码?

BASE64编码的字符进行URL传输丢失特殊字符的问题

Owin的URL编码怎么搞?以前都是HttpUtility.UrlEncode之类的,现在连system.web都没了,肿么办?