简体   繁体   English

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

[英]Uri.EscapeDataString() - Invalid URI: The Uri string is too long

I'm using compact framework/C# on windows mobile.我在 windows 移动设备上使用紧凑型框架/C#。

In my application I am uploading data to the server by serializing objects and using a HttpWebRequest/POST request to send the information up.在我的应用程序中,我通过序列化对象并使用 HttpWebRequest/POST 请求将信息向上发送来将数据上传到服务器。 On the server the post data is de-serialised and saved to the db.在服务器上,发布数据被反序列化并保存到数据库中。

The other day I realised that I had a problem with special characters in the post data (ampersands etc..).前几天我意识到我在发布数据(和号等)中遇到了特殊字符的问题。 So I introduced Uri.EscapeDataString() into the method and all was well.所以我在方法中引入了 Uri.EscapeDataString() ,一切都很好。

However, today I have discovered that there is a problem when the application attempts to upload a large amount of data (I'm unsure of what exactly denotes "large" at the moment!)但是,今天我发现当应用程序尝试上传大量数据时会出现问题(我现在不确定究竟什么是“大”!)

Existing code (Kind of)现有代码(种类)

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()));

Problem问题

The call to Uri.EscapeDataString() is causing the following exception:对 Uri.EscapeDataString() 的调用导致以下异常:

System.UriFormatException: Invalid URI: The Uri string is too long. System.UriFormatException:无效的 URI:Uri 字符串太长。

Question问题

Are there any other ways to prepare the data for upload?还有其他方法可以准备要上传的数据吗?

As far as I can see HttpUtility (which has its own Encode/Decode methods) is not available for the compact framework.据我所知,HttpUtility(它有自己的编码/解码方法)不适用于紧凑型框架。

Or you could simply split your string and call Uri.EscapeDataString(string) for each block, in order to avoid reimplementing the function.或者您可以简单地拆分您的字符串并为每个块调用Uri.EscapeDataString(string) ,以避免重新实现 function。

Sample Code:示例代码:

        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)));
            }
        }
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();

The answer of "Alberto de Paola" is good. “Alberto de Paola”的答案很好。

Nonetheless, to unescape the escaped data is little bit trickier, because you have to avoid cutting the encoded string at the middle of an encoded char (or you will break the integrity of the original string).尽管如此,要对转义的数据进行转义有点棘手,因为您必须避免在编码字符的中间切割编码字符串(否则您将破坏原始字符串的完整性)。

Here's my way of fixing this issue:这是我解决此问题的方法:

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;
}

To test these functions:要测试这些功能:

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

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

Hope this can help avoiding some headaches;)希望这可以帮助避免一些头痛;)

I have been using System.Web.HttpUtility.UrlEncode and seems to handle the longer strings much better.我一直在使用 System.Web.HttpUtility.UrlEncode 并且似乎可以更好地处理较长的字符串。

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

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

I needed another solution because the solution from Pouki does not work when Cyrillic is processed and symbol is cut.我需要另一个解决方案,因为 Pouki 的解决方案在处理 Cyrillic 并剪切符号时不起作用。

The alternative solution is as follow:替代解决方案如下:

    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();
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM