Monday, 26 October 2020

C# - HttpClient

Part 1

C# HttpClient

 

1.      C# Httpclient GET Request

2.     C# Httpclient HEAD Request

3.     C# Httpclient POST Request

4.    C# Httpclient JSON Request

5.     C# Httpclient Download Image

6.    C# Httpclient Basic Authentication

 

C# HttpClient tutorial shows how to create HTTP requests with HttpClient in C#.  The Hypertext Transfer Protocol (HTTP) is an application protocol for distributed, collaborative, hypermedia information systems. HTTP is the foundation of data communication for the World Wide Web. HttpClient is a base class for sending HTTP requests and receiving HTTP responses from a resource identified by a URI.

 

using System;
using System.Net.Http;
using System.Threading.Tasks;
 
namespace HttpClientStatus
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using var client = new HttpClient();
            var result = await client.GetAsync("http://webcode.me");
            Console.WriteLine(result.StatusCode);
        }
    }
}
 

The GetAsync() method sends a GET request to the specified Uri as an asynchronous operation. The await operator suspends the evaluation of the enclosing async method until the asynchronous operation completes. When the asynchronous operation completes, the await operator returns the result of the operation, if any.

 

C# Httpclient GET Request

The GET method requests a representation of the specified resource.

using System;
using System.Net.Http;
using System.Threading.Tasks;
 
namespace HttpClientEx
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using var client = new HttpClient();
            var content = await
                         client.GetStringAsync("http://webcode.me");
 
            Console.WriteLine(content);
        }
    }
}


The example issues a GET request to the webcode.me website. It outputs the simple HTML code of the home page.

var content = await client.GetStringAsync("http://webcode.me");

 The GetStringAsync() sends a GET request to the specified Uri and returns the response body as a string in an asynchronous operation.

$ dotnet run

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My html page</title>
</head>
<body> 
    <p>
        Today is a beautiful day. We go swimming and fishing.
    </p> 
    <p>
         Hello there. How are you?
    </p> 
</body>
</html>

C#
Httpclient HEAD Request

The HTTP HEAD method requests the headers that are returned if the specified resource would be requested with an HTTP GET method.

using System;
using System.Net.Http;
using System.Threading.Tasks;
 
namespace HttpClientHead
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var url = "http://webcode.me";
            using var client = new HttpClient();
 
            var result = await client.SendAsync(new                        
              HttpRequestMessage(HttpMethod.Head, url));
 
            Console.WriteLine(result);
        }
    }
}

 

The example issues a HEAD request.

$ dotnet run

StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1,
Content: System.Net.Http.HttpConnectionResponseContent, Headers:
{
    Server: nginx/1.6.2
    Date: Sat, 12 Oct 2019 19:55:14 GMT
    Connection: keep-alive
    ETag: "5d32ffc5-15c"
    Accept-Ranges: bytes
    Content-Type: text/html
    Content-Length: 348
    Last-Modified: Sat, 20 Jul 2019 11:49:25 GMT
}

This are the header fields of the response.

 

C# Httpclient POST Request

The HTTP POST method sends data to the server. The type of the body of the request is indicated by the Content-Type header.

$ dotnet add package Newtonsoft.Json

We need to add the Newtonsoft.Json package to process JSON data.

using System;
using System.Text;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
 
namespace HttpClientPost
{
    class Person
    {
        public string Name { get; set; }
        public string Occupation { get; set; }
 
        public override string ToString()
        {
            return $"{Name}: {Occupation}";
        }
    }
 
    class Program
    {
        static async Task Main(string[] args)
        {
            var person = new Person();
            person.Name = "John Doe";
            person.Occupation = "gardener";
 
            var json = JsonConvert.SerializeObject(person);
            var data = new StringContent(json, Encoding.UTF8,
                                        "application/json");
 
            var url = "https://httpbin.org/post";
            using var client = new HttpClient();
 
            var response = await client.PostAsync(url, data);
 
            string result =  
                   response.Content.ReadAsStringAsync().Result;
            Console.WriteLine(result);
        }
    }
}

In the example, we send a POST request to https://httpbin.org/post website, which is an online testing service for developers.

var person = new Person();
person.Name = "John Doe";
person.Occupation = "gardener";
 
var json = JsonConvert.SerializeObject(person);
var data = new StringContent(json, Encoding.UTF8, "application/json");


We turn an object into a JSON data with the help of the Newtonsoft.Json package.

var response = await client.PostAsync(url, data);

We send an asynchronous POST request with the PostAsync() method.

string result = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(result);

We read the returned data and print it to the console.

$ dotnet run
{
    "args": {},
    "data": "{\"Name\":\"John Doe\",\"Occupation\":\"gardener\"}",
    "files": {},
    "form": {},
    "headers": {
    "Content-Length": "43",
    "Content-Type": "application/json; charset=utf-8",
    "Host": "httpbin.org"
    },
    "json": {
    "Name": "John Doe",
    "Occupation": "gardener"
    },
    ...
    "url": "https://httpbin.org/post"
}

This is the output.

C# Httpclient JSON Request

JSON (JavaScript Object Notation) is a lightweight data-interchange format. This format is easy for humans to read and write and for machines to parse and generate. It is a less verbose and more readable alternative to XML. The official Internet media type for JSON is application/json.

using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json;
 
namespace HttpClientJson
{
    class Contributor
    {
        public string Login { get; set; }
        public short Contributions { get; set; }
 
        public override string ToString()
        {
            return $"{Login,20}: {Contributions} contributions";
        }
    }
 
    class Program
    {
        private static async Task Main()
        {
            using var client = new HttpClient();
 
            client.BaseAddress = new Uri("https://api.github.com");
            client.DefaultRequestHeaders.Add("User-Agent", "C# console program");
            client.DefaultRequestHeaders.Accept.Add(
                    new MediaTypeWithQualityHeaderValue("application/json"));
 
            var url = "repos/symfony/symfony/contributors";
            HttpResponseMessage response = await client.GetAsync(url);
            response.EnsureSuccessStatusCode();
            var resp = await response.Content.ReadAsStringAsync();
 
            List<Contributor> contributors =
                              JsonConvert.DeserializeObject<List<Contributor>>(resp);
            contributors.ForEach(Console.WriteLine);
        }
    }
}

The example generates a GET request to to Github. It finds out the top contributors of the Symfony framework. It uses the Newtonsoft.Json to work with JSON.

client.DefaultRequestHeaders.Add("User-Agent", "C# console program");

In the request header, we specify the user agent.

client.DefaultRequestHeaders.Accept.Add(
    new MediaTypeWithQualityHeaderValue("application/json"));

In the accept header value, we tell that JSON is an acceptable response type.

var url = "repos/symfony/symfony/contributors";
HttpResponseMessage response = await client.GetAsync(url);
var resp = await response.Content.ReadAsStringAsync();

We generate a request and read the content asynchronously.

List<Contributor> contributors = JsonConvert.DeserializeObject<List<Contributor>>(resp);
contributors.ForEach(Console.WriteLine);

We transform the JSON response into a list of Contributor objects with the JsonConvert.DeserializeObject() method.

C# Httpclient Download Image

The GetByteArrayAsync() sends a GET request to the specified Uri and returns the response body as a byte array in an asynchronous operation.

using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
 
namespace HttpClientDownloadImage
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using var httpClient = new HttpClient();
            var url = "http://webcode.me/favicon.ico";
            byte[] imageBytes = await
                                httpClient.GetByteArrayAsync(url);
 
            string documentsPath = System.Environment.GetFolderPath(
                    System.Environment.SpecialFolder.Personal);
 
 
            string localFilename = "favicon.ico";
            string localPath = Path.Combine(documentsPath,
                                            localFilename);
            File.WriteAllBytes(localPath, imageBytes);
 
        }
    }
}

In the example, we download an image from the webcode.me website. The image is written to the user's Documents folder.

byte[] imageBytes = await httpClient.GetByteArrayAsync(url);

The GetByteArrayAsync() returns the image as an array of bytes.

string documentsPath = System.Environment.GetFolderPath(
    System.Environment.SpecialFolder.Personal);

We determine the Documents folder with the GetFolderPath() method.

File.WriteAllBytes(localPath, imageBytes);

The bytes are written to the disk with the File.WriteAllBytes() method.


C# Httpclient Basic Authentication

In HTTP protocol, basic access authentication is a method for an HTTP user agent (such as a web browser or a console application) to provide a user name and password when making a request. In basic HTTP authentication, a request contains a header field in the form of Authorization: Basic <credentials>, where credentials is the base64 encoding of id and password joined by a single colon :.

Note: The credentials are not encrypted; therefore, HTTP basic authentication must be used with the HTTPS protocol.

HTTP Basic authentication is the simplest technique for enforcing access controls to web resources. It does not require cookies, session identifiers, or login pages; rather, HTTP Basic authentication uses standard fields in the HTTP header.

using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
 
namespace HttpClientDownloadImage
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using var httpClient = new HttpClient();
            var url = "http://webcode.me/favicon.ico";
            byte[] imageBytes = await
                                httpClient.GetByteArrayAsync(url);
 
            string documentsPath = System.Environment.GetFolderPath(
                    System.Environment.SpecialFolder.Personal);
 
 
            string localFilename = "favicon.ico";
            string localPath = Path.Combine(documentsPath,
                                            localFilename);
            File.WriteAllBytes(localPath, imageBytes);
 
        }
    }
}

The example sends credentials to the httpbin.org website.

var authToken = Encoding.ASCII.GetBytes($"{userName}:{passwd}");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken));

Here we build the authentication header.

var url = "https://httpbin.org/basic-auth/user7/passwd";

The URL contains authentication details because we test it with the httpbin.org website. This way we don't need to set up our own server. Authentication details are never put into the URL, of course.

$ dotnet run
{
    "authenticated": true,
    "user": "user7"
}

This is the output.

In this tutorial, we have used C# HttpClient to create HTTP requests.


Part 2

When to use WebClient vs. HttpClient vs. HttpWebRequest
Understand the pros and cons of the .Net Framework's various native classes for working with HTTP requests and responses
You have three different choices for consuming REST APIs when working in the .NET Framework: WebClient, HttpClient, and HttpWebRequest. In this post we will look at these three ways we can access REST APIs from within the managed environment, i.e., without resorting to third-party libraries. In the sections that follow I will illustrate these approaches with relevant code examples to help you gain a better understanding of the concepts.
In a nutshell, WebRequest—in its HTTP-specific implementation, HttpWebRequest—represents the original way to consume HTTP requests in .NET Framework. WebClient provides a simple but limited wrapper around HttpWebRequest. And HttpClient is the new and improved way of doing HTTP requests and posts, having arrived with .NET Framework 4.5.
Let’s start our discussion with the WebRequest abstract class.

 

System.Net.WebRequest

The System.Net.WebRequest class is an abstract class. Thus you will need to create a HttpWebRequest or FileWebRequest to consume HTTP requests using this class. The following code snippet shows how you can work with WebRequest.

WebRequest webRequest = WebRequest.Create(uri);
webRequest.Credentials = CredentialCache.DefaultCredentials;
webRequest.Method ="GET";
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
System.Net.HttpWebRequest

WebRequest was the first class provided in the .NET Framework to consume HTTP requests. It gives you a lot of flexibility in handling each and every aspect of the request and response objects, without blocking the user interface thread. You can use this class to access and work with headers, cookies, protocols, and timeouts when working with HTTP. The following code snippet illustrates how HttpWebRequest can be used.

HttpWebRequest http = HttpWebRequest)WebRequest.Create(“http://localhost:8900/api/default”);
WebResponse response = http.GetResponse();
MemoryStream memoryStream = response.GetResponseStream();
StreamReader streamReader = new StreamReader(memoryStream);
string data = streamReader.ReadToEnd();


 

System.Net.WebClient

The System.Net.WebClient class in .NET provides a high-level abstraction on top of HttpWebRequest. WebClient is just a wrapper around HttpWebRequest, so uses HttpWebRequest internally. Thus WebClient is a bit slow compared to HttpWebRequest, but requires you to write much less code. You can use WebClient for simple ways to connect to and work with HTTP services. It is generally a better choice than HttpWebRequest unless you need to leverage the additional features that HttpWebRequest provides. The following code snippet shows how you can work with WebClient.

string data = null;
using (var webClient = new WebClient())
{
    data = webClient.DownloadString(url);
}

 

System.Net.Http.HttpClient

HttpClient was introduced in .NET Framework 4.5. For developers using .NET 4.5 or later, it is the preferred way to consume HTTP requests unless you have a specific reason not to use it. In essence, HttpClient combines the flexibility of HttpWebRequest and the simplicity of WebClient, giving you the best of both the worlds.

The HttpWebRequest class provides a lot of control over the request/response object. However, you should be aware that HttpClient was never designed to be a replacement for WebClient. You should use HttpWebRequest instead of HttpClient whenever you need the additional features that HttpWebRequest provides. Further, unlike WebClient, HttpClient lacks support for progress reporting and custom URI schemes. 

Although HttpClient doesn’t support FTP, mocking and testing HttpClient is easier. All I/O bound methods in HttpClient are asynchronous, and you can use the same HttpClient instance to make concurrent requests as well. The following code snippet illustrates how you can work with HttpClient.

public async Task<Author> GetAuthorsAsync(string uri)
{
    Author author = null;
    HttpResponseMessage response = await client.GetAsync(uri);
    if (response.IsSuccessStatusCode)
    {
        author = await response.Content.ReadAsAsync<Author>();
    }
    return author;
}

Note that when there is an error in the response, HttpClient doesn’t throw an error.

Rather, it sets the IsSuccessStatusCode property to false.

If you want to throw an exception if the IsSuccessStatusCode property is false, you can make a call to the EnsureSuccessStatusCode method on the response instance as shown below.

response.EnsureSuccessStatusCode();

HttpClient was designed to be instantiated once and reused throughout the application’s lifecycle—you should not create a new HttpClient instance for every request that your application needs to process. If you do, the available sockets could become exhausted by heavy traffic, resulting in SocketException errors. The recommended practice is to create a single, shared HttpClient instance.

 

 

Calling Web Services with HttpWebRequest, WebClient and HttpClient

 

The .NET Framework gives you three different ways to call a Web Service. However, depending on your scenario, the latest and greatest object isn’t necessarily your best choice.

If you’re going to call a RESTful Web Service, the .NET Framework gives you three objects you can use to make the call: HttpWebRequest, WebClient and HttpClient. Each has its costs and benefits (of course), so this column is about why you might choose each one.

HttpWebRequest: Full Control

For me, the HttpWebRequest is primarily of "historical interest" -- it’s the earliest of the tools to appear (as I remember) and is also the least "developer-friendly" of the three objects.

The issue with the HttpWebRequest is that, if you want to call a RESTful service with the HttpWebRequest, you’re going to have to specify everything yourself. Listing 1 shows the simplest possible request using the HttpWebRequest object: a call to a single Web Service method, passing a single parameter ("Vogel"), and getting back a single, Boolean result.

Listing 1: Calling a RESTful Service Using HttpWebRequest

Dim hwr As HttpWebRequest

hwr = WebRequest.Create("http://localhost:56590/Home/ValidName?LastName=Vogel")

 

Try

  Dim wr As WebResponse

  wr = hwr.GetResponse()

 

  If CType(wr, HttpWebResponse).StatusCode = HttpStatusCode.OK Then

    Dim st As Stream

    st = wr.GetResponseStream()

    Dim sr As StreamReader

    sr = New StreamReader(st)

    Dim res As Boolean

    res = sr.ReadToEnd()

    If res Then

      '...work with result...

    End If

  End If

Catch ex as Exception

  '...handle error...

End Try

I’ve left some important steps out of here: I haven’t, for example, provided any credentials to log into the service; in C# the conversion of the returned data (the string "false") to a Boolean would be more complicated; and the WebResponse has a Dispose method that I should be calling through a Using block. But, even with those steps omitted, that’s a lot of code.

What’s good about the HttpWebRequest is that you have complete control over everything that’s sent to the Web Service. You can set the user agent string to allow your client to, for example, masquerade as an iPhone.

WebClient: Ease of Use

The WebClient object addresses the complexity of the HttpWebRequest by wrapping it to provide a simpler interface for calling RESTful services. Listing 2 does the same thing as Listing 1, but requires far less code.

Listing 2: Calling a RESTful Service Using WebClient

 

Dim wc As WebClient

wc = New WebClient

Try

  Dim res As Boolean 

  res = wc.DownloadString("http://localhost:56590/Home/ValidName?LastName=Vogel")

  If res Then

    '...work with result...

  End If

Catch ex As Exception

  '...handle error...

End Try

 

In addition to simplifying requests, the WebClient provides methods for asynchronously calling Web Services (both in uploading and downloading). And, while you don’t have quite the low-level control that the HttpWebRequest does, it still gives you (for example) the ability to manipulate the headers that you send to the Web Service.

If you’re using a version of the .NET Framework earlier than 4.5, then the WebClient should be your first choice. You should use the HttpWebRequest only if you find there’s something you need that the WebClient won’t do.

HttpClient: Asynchronous Heaven

However, if you are using the .NET Framework version 4.5 or later (and version 4.5 has been out for five years now), then you also have access to the HttpClient object. The HttpClient provides functionality that neither WebClient nor HttpWebRequest does. For example, with HttpClient you can make multiple requests without having to create a new object; you can track the progress of long-running requests; and it’s a lot easier to integrate the HttpClient into testing through mocking.

The downside of using the HttpClient is that every method is asynchronous. If you use HttpClient in a pseudo-synchronous mode, the code can be a little ugly, as Listing 3 shows, which has almost as many lines of code as the original HttpWebRequest object required.

Listing 3: Calling a RESTful Service Using HttpClient Synchronously

Dim hc As HttpClient

hc = New HttpClient

Try

  Dim trm As Task(Of HttpResponseMessage)

  trm = hc.GetAsync("http://localhost:56590/Home/ValidName?LastName=Vogel")

  Dim rm As HttpResponseMessage

  rm = trm.Result

  If rm.IsSuccessStatusCode Then

    Dim ts As Task(Of String)

    ts = rm.Content.ReadAsStringAsync

    Dim res As Boolean

    res = ts.Result

    If res Then

      '...work with result...

    End If

  End If

Catch ex As Exception

  '...handle exception...

End Try

However, if you use HttpClient as it was intended (asynchronously), then the code gets simpler. If I use await and async, as shown in Listing 4, the code isn’t quite as short as with the WebClient … but it’s close and it’s asynchronous.

Listing 4: Calling a RESTful Service Using HttpClient Asynchronously

Dim hc As HttpClient

hc = New HttpClient

Try

  Dim rm As HttpResponseMessage

  rm = Await hc.GetAsync("http://localhost:56590/Home/ValidName?LastName=Vogel")

  If rm.IsSuccessStatusCode Then

    Dim res As Boolean

    res = Await rm.Content.ReadAsStringAsync

    If res Then

      '...work with a result...

    End If

  End If

Catch ex As Exception

  '...work with exception...

End Try

So, what is my best choice? If I’m happy with synchronous code and don’t need to mock my RESTful calls when testing, I’m still using the WebClient. However, if I can leverage asynchronous code, have a long-running process I want to track, or need to mock calls for testing, then I use HttpClient. And, on those very rare occasions when I need to do something odd with the HTTP protocol, I fall back on the HttpWebRequest object.

You can’t, after all, have too many tools.

 


No comments:

Post a Comment