50

I'm writing a small API-connected application in C#.

I connect to a API which has a method that takes a long string, the contents of a calendar(ics) file.

I'm doing it like this:

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(URL);
request.Method = "POST";
request.AllowAutoRedirect = false;
request.CookieContainer = my_cookie_container;
request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
request.ContentType = "application/x-www-form-urlencoded";

string iCalStr = GetCalendarAsString();

string strNew = "&uploadfile=true&file=" + iCalStr;

using (StreamWriter stOut = new StreamWriter(request.GetRequestStream(), System.Text.Encoding.ASCII))
 {
     stOut.Write(strNew);
     stOut.Close();
 }

This seems to work great, until I add some specific HTML in my calendar.

If I have a '&nbsp' somewhere in my calendar (or similar) the server only gets all the data up to the '&'-point, so I'm assuming the '&' makes it look like anything after this point belongs to a new parameter?

How can I fix this?

nelshh
  • 1,021
  • 1
  • 11
  • 15

3 Answers3

59

First install "Microsoft ASP.NET Web API Client" nuget package:

  PM > Install-Package Microsoft.AspNet.WebApi.Client

Then use the following function to post your data:

public static async Task<TResult> PostFormUrlEncoded<TResult>(string url, IEnumerable<KeyValuePair<string, string>> postData)
{
    using (var httpClient = new HttpClient())
    {
        using (var content = new FormUrlEncodedContent(postData))
        {
            content.Headers.Clear();
            content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");

            HttpResponseMessage response = await httpClient.PostAsync(url, content);

            return await response.Content.ReadAsAsync<TResult>();
        }
    }
}

And this is how to use it:

TokenResponse tokenResponse = 
    await PostFormUrlEncoded<TokenResponse>(OAuth2Url, OAuth2PostData);

or

TokenResponse tokenResponse = 
    (Task.Run(async () 
        => await PostFormUrlEncoded<TokenResponse>(OAuth2Url, OAuth2PostData)))
        .Result

or (not recommended)

TokenResponse tokenResponse = 
    PostFormUrlEncoded<TokenResponse>(OAuth2Url, OAuth2PostData).Result;
Tohid
  • 6,175
  • 7
  • 51
  • 80
  • Not sure if I am missing something, but this way the request hangs – deanwilliammills Aug 12 '19 at 12:08
  • What is `OAuth2PostData` ? – Kiquenet May 21 '20 at 10:37
  • @Kiquenet - When you post a request with `Content-Type` of `application/x-www-form-urlencoded` it usually required some data. I copied the sample code from a code that was written for OAuth2 authentication so the varible name is `OAuth2PostData`. The name is not important, it is simply a variable that holds data. It is basically a list of `KeyValuePair`. – Tohid May 21 '20 at 10:53
  • Sorry but how to use `TokenResponse` class? Add `using Microsoft.Bot.Schema;` ? – Pham X. Bach Nov 01 '22 at 02:33
  • 1
    @PhamX.Bach - `TokenResponse` is your model. Define a class that matches the return schema of the Web API which you are calling. It is a simple POCO. – Tohid Nov 03 '22 at 05:58
32

Since your content-type is application/x-www-form-urlencoded you'll need to encode the POST body, especially if it contains characters like & which have special meaning in a form.

Try passing your string through HttpUtility.UrlEncode before writing it to the request stream.

Here are a couple links for reference.

confusedandamused
  • 746
  • 2
  • 8
  • 28
Andy White
  • 86,444
  • 48
  • 176
  • 211
10

As long as the server allows the ampresand character to be POSTed (not all do as it can be unsafe), all you should have to do is URL Encode the character. In the case of an ampresand, you should replace the character with %26.

.NET provides a nice way of encoding the entire string for you though:

string strNew = "&uploadfile=true&file=" + HttpUtility.UrlEncode(iCalStr);
Justin Niessner
  • 242,243
  • 40
  • 408
  • 536