HttpClient: The uri string is too long
Asked Answered
W

5

37

Given the following attempt to post data to a web service that generates PDF files, PDF rocket (which is awesome by the way).

I get the error Invalid URI: The uri string is too long
Why would anyone impose an arbitrary limit on POSTed data?

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

I receive this rediculous error.

 {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

This reminds me of 640k ought to be enough... I mean really?

Wanhsien answered 18/7, 2016 at 15:29 Comment(6)
With a post can include the content in the http message instead of the URI. A uri has a max length of 2083 characters.Ernaldus
aaah, that makes sense, how?Wanhsien
Also why use a web service to make PDF from HTML when you can do it yourself with a free library?Cimabue
See this so question, in this instance they send it as json in the http message. There is no limitation on data length when it is done this way. Create a json string using JsonConvert.SerializeObject and then send as string content using StringContent and send that.Ernaldus
Am I therefore correct in saying that there is no built in equivalent in HttpClient for UploadValues("https://api.html2pdfrocket.com/pdf", options) in the WebClient class?Wanhsien
@Cimabue - tried a plethora of free ones that did not pass muster, and for our volumes we don't currently pay for pdf rocket anyway.Wanhsien
E
25

With a post can include the content in the http message instead of the URI. A uri has a max length of 2083 characters. You could send it as JSON in the http message instead of the URI which is the recommended way to send larger chunks of data in an HttpPost/HttpPut. I altered your code to make use of it. This assumes that your service you are contacting can work with JSON (.net Web Api out of the box should have no problem with this).

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

Make sure to install newtonsoft json.

Ernaldus answered 18/7, 2016 at 15:39 Comment(7)
thanks, I didn't read the name of the class carefully enough, I thought it was already a regular post. Thanks.Wanhsien
it would be nice to know if there's a built in equivalent to the WebClient, UploadValues method, which doesn't use json, and still manage to post all the content.Wanhsien
@Wanhsien - this SO answer might be the approach you are looking for.Ernaldus
thanks, that example posts a single value, but the principle applies - crazy it's not built in - thanks for your help.Wanhsien
as a matter of interest, the data I am posting is actually not in the url, it's posted exactly like StringContent. The error is not in using an incorrect method, the error is in an arbitrary limit on the EscapeDataString that FormUrlEncodedContent happens to use.Wanhsien
If you have a look at the UriFormatException as described at the Uri(string) constructor reference, you'll see that the maximum length of a URI string is 65519 characters, while the scheme of the URI string should not exceed 1023 characters.Edp
"With a post can include the content in the http message instead of the URI." <- What does this mean? I cannot parse this sentence.Lysol
C
52

If, like me, you're faced with some wonky 3rd party web service that will only accept form content, you can work around the problem like this:

// 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);
Cleanlimbed answered 15/8, 2018 at 7:7 Comment(3)
Best answer here. Needs upvoting.Foetus
This saved the day!Pittsburgh
Note that Uri.EscapeDataString conforms to RFC2986 (for example, "!" is escaped and spaces are hexified) but WebUtility.UrlEncode doesn't ("!" is unaltered, spaces become "+").Tennessee
E
25

With a post can include the content in the http message instead of the URI. A uri has a max length of 2083 characters. You could send it as JSON in the http message instead of the URI which is the recommended way to send larger chunks of data in an HttpPost/HttpPut. I altered your code to make use of it. This assumes that your service you are contacting can work with JSON (.net Web Api out of the box should have no problem with this).

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

Make sure to install newtonsoft json.

Ernaldus answered 18/7, 2016 at 15:39 Comment(7)
thanks, I didn't read the name of the class carefully enough, I thought it was already a regular post. Thanks.Wanhsien
it would be nice to know if there's a built in equivalent to the WebClient, UploadValues method, which doesn't use json, and still manage to post all the content.Wanhsien
@Wanhsien - this SO answer might be the approach you are looking for.Ernaldus
thanks, that example posts a single value, but the principle applies - crazy it's not built in - thanks for your help.Wanhsien
as a matter of interest, the data I am posting is actually not in the url, it's posted exactly like StringContent. The error is not in using an incorrect method, the error is in an arbitrary limit on the EscapeDataString that FormUrlEncodedContent happens to use.Wanhsien
If you have a look at the UriFormatException as described at the Uri(string) constructor reference, you'll see that the maximum length of a URI string is 65519 characters, while the scheme of the URI string should not exceed 1023 characters.Edp
"With a post can include the content in the http message instead of the URI." <- What does this mean? I cannot parse this sentence.Lysol
O
13

I just solved a similar problem. For me I was integrating with a backend I didn't control and had to POST file along with form data (eg customerID) as form variables. So switching to JSON or Multipart would break the backend I didn't control. The problem was that large files would cause the FormUrlEncodedContent to throw an error saying "The uri string is too long".

This is the code that solved it for me after two days of effort (note still needs to be tweaked to be ASYNC).

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



        }

    }
Oilcloth answered 23/1, 2018 at 3:52 Comment(0)
R
7

@Mick Byrne : Thanks - your solution worked like a charme!

Here is my complete code:

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



    }
Rose answered 13/10, 2018 at 14:46 Comment(0)
R
-1

Completely working code, in Form URL Encoded format:

using (HttpClient httpClient = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Post, "your_postUrl")
var formUrlEncodedData = ToFormUrlEncoded(collection);
var content = new StringContent("your_content_payload",Encoding.UTF8,"application/x-www-form-urlencoded");
request.Content = content;
var response = await httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
return result = await response.Content.ReadAsStringAsync();
}

static string ToFormUrlEncoded(Dictionary<string, string> data)
{
    var formData = new StringBuilder();
    foreach (var kvp in data)
    {
        if (formData.Length > 0)
            formData.Append('&');
        formData.Append($"{WebUtility.UrlEncode(kvp.Key)}={WebUtility.UrlEncode(kvp.Value)}");
    }
    return formData.ToString();
}
Rustie answered 1/4 at 12:5 Comment(1)
Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. Would you kindly edit your answer to include additional details for the benefit of the community?Stralsund

© 2022 - 2024 — McMap. All rights reserved.