Share via


Switching from HttpClient to WebClient

Question

Saturday, November 26, 2016 8:36 PM

Hi everybody,

I had code which I tested and it used to work, but stop working today. It is now being send as text/plain instead of application/json. Also, I was advised to use WebClient instead of HttpClient because I really don't want to use asynchronous call but rather normal synchronous. I would need to call my dll from the legacy code (as well as from another C# dll).

I've been trying to find some concise and good example of using either WebClient or HttpWebRequest to do the same my current code does, but I am unable to find a really full complete example. So, I am asking to help me to convert my current code to use either WebClient or HttpWebRequest class.

Here is my current code

 async Task<TicketUsageResponse> GetResponse(CityPassJSON json, String url)
        {
            TicketUsageResponse ticketUsageResponse = null;


            using (HttpResponseMessage response = await client.PostAsJsonAsync(url, json))
            {
                if (response.IsSuccessStatusCode)
                {
                    using (HttpContent content = response.Content)
                    {
                        ticketUsageResponse = await content.ReadAsAsync<TicketUsageResponse>();
                    }
                }
                else
                {
                    Int32 statusCode = (Int32)response.StatusCode;
                    throw new HttpException(statusCode, response.ReasonPhrase);
                }

            }
            return ticketUsageResponse;
        }

        private void SetRequestHeaders(string tcHost, string tcUserName, string tcPassword, int tnTimeout)
        {
            client.DefaultRequestHeaders.Accept.Clear();
            client.BaseAddress = new Uri(tcHost);
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            
            client.DefaultRequestHeaders.Authorization =
                        new AuthenticationHeaderValue(
        "Basic",
        Convert.ToBase64String(
            System.Text.ASCIIEncoding.ASCII.GetBytes(
                string.Format("{0}:{1}", tcUserName, tcPassword))));

            //  client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue() { MaxAge = TimeSpan.Zero };
            client.Timeout = TimeSpan.FromSeconds(tnTimeout);
        }

 

This code no longer works for me and I'm not sure exactly why it used to work and no longer works. But in either case, I need to use one of the mentioned classes instead of HttpClient class.

Thanks a lot in advance.

For every expert, there is an equal and opposite expert. - Becker's Law

My blog

My TechNet articles

All replies (8)

Sunday, November 27, 2016 2:54 AM ✅Answered | 1 vote

Hi Naomi N,

I have rewritten your code using HttpWebRequest. I change the type of parameter and return type to string instead of CityPassJSON and TicketUsageResponse. You need to use some serialization and deserialization operations before using these methods.

private HttpWebRequest request;

public string GetResponse(string json, String url)
{
    request = WebRequest.Create(url) as HttpWebRequest;
    request.Method = "Post";
    request.ContentType = "application/json;charset=UTF-8";
    Stream stream = request.GetRequestStream();
    byte[] buffer = Encoding.UTF8.GetBytes(json);
    stream.Write(buffer, 0, buffer.Length);

    HttpWebResponse response = request.GetResponse() as HttpWebResponse;
    if (response.StatusCode == HttpStatusCode.OK)
    {
        Stream responseStream = response.GetResponseStream();
        using (StreamReader sr = new StreamReader(responseStream))
        {
            return sr.ReadToEnd();
        }
    }
    else
    {
        throw new Exception(response.StatusDescription);
    }
}

private void SetRequestHeaders(string tcHost, string tcUserName, string tcPassword, int tnTimeout)
{
    request.Host = tcHost;
    request.Headers.Remove("Accept");
    request.Headers.Add("Accept", "application/json");
    string authorizationKey = Convert.ToBase64String(
     System.Text.ASCIIEncoding.ASCII.GetBytes(
         string.Format("{0}:{1}", tcUserName, tcPassword)));
    request.Headers.Add("Authorization", "Basic "+ authorizationKey);
    request.Timeout = tnTimeout;
}

>>"This code no longer works for me and I'm not sure exactly why it used to work and no longer works."

Is there any error or exceptions pop ups? I suggest you use a HTTP monitor software(for example Fiddler) to view raw request message and response message.  You will see what is happened in the message passing process.

Best Regards,
Li Wang

MSDN Community Support
Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact [email protected].


Monday, November 28, 2016 5:32 AM

Thanks a lot, I'll try your code. Do you have suggestions as what to use for converting object to string and back from string to json for completeness of that code?

It used to work because the request was send as json before. But all of the sudden the request started to being send as text/plain and therefore it was slightly different looking than the original way it was sending. So, now it produces a bad request. I was monitoring it in Fiddler. I tried changing my code a bit but nothing I tried was able to send my request as application/json. That's the main problem with my original code.

For every expert, there is an equal and opposite expert. - Becker's Law

My blog

My TechNet articles


Monday, November 28, 2016 5:35 AM | 1 vote

Hi Naomi,

>>"Do you have suggestions as what to use for converting object to string and back from string to json for completeness of that code?"

JSON.NET is a good library, you could install it from NuGet by searching JSON.NET. Here are the samples of using JSON.NET.

Serialize an Object

Deserialize an Object

Best Regards,
Li Wang

MSDN Community Support
Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact [email protected].


Monday, November 28, 2016 9:18 AM

I'm trying to incorporate your code into my original code but I'm thinking it's not going to work as is. In my original code I first set the request headers and then called GetResponse. So this is my current code

 Int32 statusCode = 1;
            Int32 nTries = 0;
            try
            {
                SetRequestHeaders(tcHost, tcUserName, tcPassword, tnTimeout);

                var json = new CityPassJSON();

                json.TicketUsageRequests = new List<TicketUsageRequest>();
                json.TicketUsageRequests.Add(new TicketUsageRequest(tcBarcode, tcUsageDate, tcUserName));

                String jsonString = JsonConvert.SerializeObject(json);

                TicketUsageResponse responseObject = null;

                while (nTries <= tnRetries)
                {

                    String result = GetResponse(jsonString, url);

                    try
                    {
                        responseObject = JsonConvert.DeserializeObject<TicketUsageResponse>(result); ;
                    }

                    catch (AggregateException exception)
                    {
                        if (exception.InnerExceptions.OfType<TaskCanceledException>().A‌​ny())
                        {
                            nTries++; // increment number of tries
                            if (nTries > tnRetries)
                            {
                                throw exception;
                            }
                        }
                        else
                        {
                            throw exception;
                        }
                    }
                    catch (TaskCanceledException te)
                    {
                        nTries++; // increment number of tries
                        if (nTries > tnRetries)
                        {
                            throw te;
                        }
                    }
                    catch (WebException we)
                    {
                        if (we.Status == WebExceptionStatus.Timeout && nTries < tnRetries)
                        {
                            nTries++; // increment number of tries
                        }
                        else
                        {
                            throw we; // re-throw exception
                        }
                    }

                    if (responseObject != null)
                    {
                        tcMessage = responseObject.ReturnData[0].Message;
                        tcReturnValueText = responseObject.ReturnValueText;
                        tnReturnValue = responseObject.ReturnValue;
                        tcProductCode = responseObject.ReturnData[0].ExternalProductCode;
                        break; // from the while loop
                    }
                }
            }

In this code url is a part of url (without the Host - just the API url).

I think this code is not going to work as the request is not yet defined when SetRequestHeaders method is called. Do you see what do I need to change?

I'll add the test methods in the meantime.

For every expert, there is an equal and opposite expert. - Becker's Law

My blog

My TechNet articles


Monday, November 28, 2016 12:08 PM

Hi,

The SetRequestHeaders method is throwing exceptions. I commented out the Host method, then commented out the Remove line, but then the next line of code is still throwing the exception Message = "The 'Accept' header must be modified using the appropriate property or method.\r\nParameter name: name". 

Found this discussion http://stackoverflow.com/questions/239725/cannot-set-some-http-headers-when-using-system-net-webrequest, so I may try implementing that solution through static extension class.

For every expert, there is an equal and opposite expert. - Becker's Law

My blog

My TechNet articles


Monday, November 28, 2016 12:41 PM

I've changed the SetRequestHeaders method to the following:

private void SetRequestHeaders(string tcHost, string tcUserName, string tcPassword, int tnTimeout, string url)
        {
            request = WebRequest.Create(tcHost + url) as HttpWebRequest;
        //    request.Host = tcHost;
       //     request.Headers.Remove("Accept");
            request.Accept = "application/json";
            string authorizationKey = Convert.ToBase64String(
             System.Text.ASCIIEncoding.ASCII.GetBytes(
                 string.Format("{0}:{1}", tcUserName, tcPassword)));
            request.Headers.Add("Authorization", "Basic " + authorizationKey);
            request.Timeout = tnTimeout * 1000;  
        }

The GetResponse method is now:

private string GetResponse(string json)
        {
            //request = WebRequest.Create(url) as HttpWebRequest;

            String responseString;
            request.Method = "Post";
            request.ContentType = "application/json;charset=UTF-8";
            Stream stream = request.GetRequestStream();
            byte[] buffer = Encoding.UTF8.GetBytes(json);
            stream.Write(buffer, 0, buffer.Length);

            HttpWebResponse response = request.GetResponse() as HttpWebResponse;
            if (response.StatusCode == HttpStatusCode.OK)
            {
                Stream responseStream = response.GetResponseStream();
                using (StreamReader sr = new StreamReader(responseStream))
                {
                    responseString = sr.ReadToEnd();
                }
            }
            else
            {
                throw new Exception(response.StatusDescription);
            }

            response.Close();
            return responseString;
        }

But unfortunately I'm getting the same 'Bad Request' as I was getting with the HttpClient :( I am supposed to send the following:

POST /integrationapi//Attraction/Tickets/Validate HTTP/1.1

Host: dev.citypass.com

Content-Type: application/json

Authorization: Basic "string here"

Cache-Control: no-cache

Postman-Token: "some token here"

 

{

  "TicketUsageRequests": [

    {

      "TicketBarcode": "barcodehere",

      "TicketUsageDate": "2016-10-28T15:09:06.7021957-06:00",

      "UserID": "namehere"

}

  ]

I am sending the following (I see in the fiddler):

POST https://dev.citypass.com//integrationapi//Attraction/Tickets/Use HTTP/1.1
Accept: application/json
Authorization: Basic "string here"
Content-Type: application/json;charset=UTF-8
Host: dev.citypass.com
Content-Length: 143
Expect: 100-continue
Connection: Keep-Alive

{"TicketUsageRequests":[{"TicketBarcode":"barcodehere","TicketUsageDate":"2016-10-28T15:09:06.7021957-06:00","UserID":"namehere"}]}

Do you see what is wrong here and what should I adjust?

I know my original class (HttpClient) was working at some point and then stopped.

I'll get my original from the TFS server, re-start my laptop and re-try original code.

Thanks a lot in advance.

UPDATE. Re-tried my original code after re-starting laptop, but still getting bad request. 

For every expert, there is an equal and opposite expert. - Becker's Law

My blog

My TechNet articles


Tuesday, November 29, 2016 7:46 AM

Hi Naomi,

Thank you for your feedback.

>>"Do you see what is wrong here and what should I adjust?"

Your code is right. The error maybe caused by server side. Have you contact the service provided for dedicated support?

Best Regards,
Li Wang

MSDN Community Support
Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact [email protected].


Tuesday, November 29, 2016 10:31 AM

Hi Li,

It was indeed the problem on their end which is now corrected and both versions of the code work fine. I did waste a lot of time in my tries, though, before I contacted the developers of the service API (as I was thinking the problem on my end).

Thanks a lot for your help.

For every expert, there is an equal and opposite expert. - Becker's Law

My blog

My TechNet articles