HttpClient:URI字符串太长。

33

考虑以下尝试向生成PDF文件的Web服务提交数据:PDF Rocket(顺便说一句,这很棒)。

我得到了错误消息Invalid URI: The uri string is too long
为什么有人会对POST的数据强加任意限制呢?

using (var client = new HttpClient())
{
    // Build the conversion options
    var options = new Dictionary<string, string>
    {
        { "value", html },
        { "apikey", ConfigurationManager.AppSettings["pdf:key"] },
        { "MarginLeft", "10" },
        { "MarginRight", "10" }
    };

    // THIS LINE RAISES THE EXCEPTION
    var content = new FormUrlEncodedContent(options);

    var response = await client.PostAsync("https://api.html2pdfrocket.com/pdf", content);
    var result = await response.Content.ReadAsByteArrayAsync();
    return result;
}

我收到了这个荒谬的错误。

 {System.UriFormatException: Invalid URI: The Uri string is too long.
   at System.UriHelper.EscapeString
   at System.Uri.EscapeDataString
   at System.Net.Http.FormUrlEncodedContent.Encode
   at System.Net.Http.FormUrlEncodedContent.GetContentByteArray

这使我想起了“640k 应该足够用了…” 我的意思是,真的吗?


一个POST请求可以将内容包含在HTTP消息中,而不是URI中。URI的最大长度为2083个字符。 - Igor
啊,那就有道理了,怎么做到的? - Jim
1
为什么要使用Web服务从HTML生成PDF,而不是使用免费库自己完成呢? - DavidG
请参考这个stackoverflow问题,在该情况下,他们将其作为JSON发送到HTTP消息中。以此方式发送时数据长度没有限制。使用JsonConvert.SerializeObject创建一个JSON字符串,然后使用StringContent作为字符串内容发送即可。 - Igor
那么我这样说就对了,HttpClient类中没有内置的等价方法可以替代WebClient类中的UploadValues("https://api.html2pdfrocket.com/pdf", options)方法,是吗? - Jim
@DavidG - 我们尝试了大量的免费工具,但它们都不符合要求,而且对于我们的规模,我们目前也没有支付PDF Rocket的费用。 - Jim
4个回答

50

如果和我一样,你面对的是一些有问题的第三方网络服务,只接受表单内容,那么你可以通过以下方式解决问题:

// Let's assume you've got your key-value pairs organised into a nice Dictionary<string, string> called formData
var encodedItems = formData.Select(i => WebUtility.UrlEncode(i.Key) + "=" + WebUtility.UrlEncode(i.Value));
var encodedContent = new StringContent(String.Join("&", encodedItems), null, "application/x-www-form-urlencoded");

// Post away!
var response = await client.PostAsync(url, encodedContent);

9
最佳答案在这里,请给予支持。 - Tom Troughton
1
这拯救了全局! - Dani Duran
1
请注意,Uri.EscapeDataString 符合 RFC2986(例如,“!” 被转义,空格被十六进制编码),但 WebUtility.UrlEncode 不符合(“!” 保持不变,空格变成“+”)。 - Daz

25

使用POST方法时,可以将内容放在HTTP消息正文中而非URI中。URI最大长度为2083个字符。您可以将其作为JSON发送到HTTP消息中,而不是URI,这是在HttpPost / HttpPut中发送较大数据块的推荐方法。我修改了您的代码以利用此功能。这假设您正在联系的服务可以使用JSON(.net Web Api应该没有问题)。

using (var client = new HttpClient())
{
    // Build the conversion options
    var options = new 
    {
        value = html,
        apikey = ConfigurationManager.AppSettings["pdf:key"],
        MarginLeft = "10",
        MarginRight = "10"
    };

    // Serialize our concrete class into a JSON String
    var stringPayload = JsonConvert.SerializeObject(options);
    var content = new StringContent(stringPayload, Encoding.UTF8, "application/json");

    var response = await client.PostAsync("https://api.html2pdfrocket.com/pdf", content);
    var result = await response.Content.ReadAsByteArrayAsync();
    return result;
}
确保安装了newtonsoft json

1
谢谢,我没有仔细阅读类的名称,我以为它已经是一个常规帖子了。谢谢。 - Jim
知道是否有内置等效于“WebClient”、“UploadValues”方法的内容,它不使用json,仍然可以发布所有内容,这将是很好的。 - Jim
@Jim - 你可能需要查看这篇Stack Overflow答案(https://dev59.com/J14c5IYBdhLWcg3wyM0B#27548828)。 - Igor
谢谢,那个例子只发布了一个值,但原则适用 - 疯狂的是它没有内置 - 感谢您的帮助。 - Jim
13
有趣的是,我发布的数据实际上不是在URL中,而是像StringContent一样发布的。错误不在于使用了错误的方法,而在于FormUrlEncodedContent恰好使用了EscapeDataString的任意限制。 - Jim
显示剩余2条评论

13

我刚解决了一个类似的问题。对我来说,我正在集成一个我无法控制的后端,并且必须将文件与表单数据(例如客户ID)一起作为表单变量POST。因此,切换到JSON或Multipart将破坏我无法控制的后端。问题是大文件会导致FormUrlEncodedContent抛出错误,显示“uri字符串过长”。

这是在两天的努力后为我解决问题的代码(请注意,仍需要进行调整以实现异步)。

private string UploadFile(string filename, int CustomerID, byte[] ImageData) {

        string Base64String = "data:image/jpeg;base64," + Convert.ToBase64String(ImageData, 0, ImageData.Length);

        var baseAddress = new Uri("[PUT URL HERE]");
        var cookieContainer = new CookieContainer();
        using (var handler = new HttpClientHandler() { AllowAutoRedirect = true, UseCookies = true, CookieContainer = cookieContainer })
        using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
        {

            try {

                //ENCODE THE FORM VARIABLES DIRECTLY INTO A STRING rather than using a FormUrlEncodedContent type which has a limit on its size.        
                string FormStuff = string.Format("name={0}&file={1}&id={2}", filename, HttpUtility.UrlEncode(Base64String), CustomerID.ToString());
                //THEN USE THIS STRING TO CREATE A NEW STRINGCONTENT WHICH TAKES A PARAMETER WHICH WILL FormURLEncode IT AND DOES NOT SEEM TO THROW THE SIZE ERROR
                StringContent content = new StringContent(FormStuff, Encoding.UTF8, "application/x-www-form-urlencoded");

                //UPLOAD
                string url = string.Format("/ajax/customer_image_upload.php");
                response = client.PostAsync(url, content).Result;
                return response.Content.ToString();

            }
            catch (Exception ex) {
                return ex.ToString();
            }



        }

    }

6

@Mick Byrne: 谢谢 - 你的解决方案很好用!

这是我的完整代码:

      public async Task DateienSendenAsync (string PfadUndDatei, string Dateiname, String VRPinGUID, String ProjektGUID, String VRPinX, String VRPinY, String VRPinZ)
    {
        var client = new HttpClient();
        // Create the HttpContent for the form to be posted.
        var requestContent = new[] {
                            new KeyValuePair<string, string>("dateiname", Dateiname),

                            new KeyValuePair<string, string>("bild", Convert.ToBase64String(File.ReadAllBytes(PfadUndDatei))),
                            new KeyValuePair<string, string>("VRPinGUID", VRPinGUID),
                            new KeyValuePair<string, string>("ProjektGUID", ProjektGUID),
                            new KeyValuePair<string, string>("ebene", "ebene"),
                            new KeyValuePair<string, string>("raumnummer", "raumnummer"),
                            new KeyValuePair<string, string>("ansichtsname", "ansichtsname"),
                            new KeyValuePair<string, string>("VRPinX", VRPinX),
                            new KeyValuePair<string, string>("VRPinY", VRPinY),
                            new KeyValuePair<string, string>("VRPinZ", VRPinZ),

                            };

        String url = "http://yourhomepage/path/upload.php";

        var encodedItems = requestContent.Select(i => WebUtility.UrlEncode(i.Key) + "=" + WebUtility.UrlEncode(i.Value));
        var encodedContent = new StringContent(String.Join("&", encodedItems), null, "application/x-www-form-urlencoded");

        // Post away!
        var response = await client.PostAsync(url, encodedContent);



    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接