As you might have already heard and tried out with .NET 4.5 (or so) Microsoft blessed us with a new and shiny HttpClient that should be easier to use, support async programming and (that’s best) finally allow the user to set any headers without reverting to some workaround code (cf. WebClient and its underlying classes).

And it is actually true – once you get over the some kind of misleading or lacking documentation. Have a look at your favourite search engine’s search results and you will know what I mean…

Let me show you a quick example that does CRUD (POST/GET/PUT/DELETE) in a synchronous way and also sets the Content-Type, Accept header without reverting to construct a dedicated HttpRequestMessage object:


// HttpClient and how to use Headers, Content-Type and PostAsync
// https://d-fens.ch/2014/04/12/httpclient-and-how-to-use-headers-content-type-and-postasync/
// Copyright 2014-2015 Ronald Rink, d-fens GmbH
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// NOTICE
// d-fens HttpClient
// this software contains work developed at
// d-fens GmbH, General-Guisan-Strasse 6, CH-6300 Zug, Switzerland
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
//using System.Json;
string Invoke(
string Method
,
string Uri
,
string Body
)
{
var cl = new HttpClient();
cl.BaseAddress = new Uri(Uri);
int _TimeoutSec = 90;
cl.Timeout = new TimeSpan(0, 0, _TimeoutSec);
string _ContentType = "application/json";
cl.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(_ContentType));
var _CredentialBase64 = "RWRnYXJTY2huaXR0ZW5maXR0aWNoOlJvY2taeno=";
cl.DefaultRequestHeaders.Add("Authorization", String.Format("Basic {0}", _CredentialBase64));
var _UserAgent = "d-fens HttpClient";
// You can actually also set the User-Agent via a built-in property
cl.DefaultRequestHeaders.Add("User-Agent", _UserAgent);
// You get the following exception when trying to set the "Content-Type" header like this:
// cl.DefaultRequestHeaders.Add("Content-Type", _ContentType);
// "Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects."
HttpResponseMessage response;
var _Method = new HttpMethod(Method);
switch (_Method.ToString().ToUpper())
{
case "GET":
case "HEAD":
// synchronous request without the need for .ContinueWith() or await
response = cl.GetAsync(Uri).Result;
break;
case "POST":
{
// Construct an HttpContent from a StringContent
HttpContent _Body = new StringContent(Body);
// and add the header to this object instance
// optional: add a formatter option to it as well
_Body.Headers.ContentType = new MediaTypeHeaderValue(_ContentType);
// synchronous request without the need for .ContinueWith() or await
response = cl.PostAsync(Uri, _Body).Result;
}
break;
case "PUT":
{
// Construct an HttpContent from a StringContent
HttpContent _Body = new StringContent(Body);
// and add the header to this object instance
// optional: add a formatter option to it as well
_Body.Headers.ContentType = new MediaTypeHeaderValue(_ContentType);
// synchronous request without the need for .ContinueWith() or await
response = cl.PutAsync(Uri, _Body).Result;
}
break;
case "DELETE":
response = cl.DeleteAsync(Uri).Result;
break;
default:
throw new NotImplementedException();
break;
}
// either this – or check the status to retrieve more information
response.EnsureSuccessStatusCode();
// get the rest/content of the response in a synchronous way
var content = response.Content.ReadAsStringAsync().Result;
return content;
}

view raw

HttpClient.cs

hosted with ❤ by GitHub


// d-fens HttpClient
// this software contains work developed at
// d-fens GmbH, General-Guisan-Strasse 6, CH-6300 Zug, Switzerland

view raw

NOTICE

hosted with ❤ by GitHub

In case the code is not displayed correctly you can view it at: https://gist.github.com/dfch/7b338046d5e63e3b3106

using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Json;

String Invoke(
  String Method
  ,
  String Uri
  ,
  String Body
  )
{
  HttpClient cl = new HttpClient();
  cl.BaseAddress = new Uri(Uri);
  int _TimeoutSec = 90;
  cl.Timeout = new TimeSpan(0, 0, _TimeoutSec);
  string _ContentType = "application/json";
  cl.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(_ContentType));
  _CredentialBase64 = "RWRnYXJTY2huaXR0ZW5maXR0aWNoOlJvY2taeno="
  cl.DefaultRequestHeaders.Add("Authorization", String.Format("Basic {0}", _CredentialBase64));
  _UserAgent = "d-fens HttpClent";
  # You can actually also set the User-Agent via a built-in property
  cl.DefaultRequestHeaders.Add("User-Agent", _UserAgent);
  # You get the following exception when trying to set the "Content-Type" header like this:
  # cl.DefaultRequestHeaders.Add("Content-Type", _ContentType);
  # "Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects."

  HttpResponseMessage response;
  HttpMethod _Method = new HttpMethod(Method);

  switch (_Method.ToString().ToUpper())
  {
    case "GET":
    case "HEAD":
      # synchronous request without the need for .ContinueWith() or await
      response = cl.GetAsync(Uri).Result;
      break;
    case "POST":
      {
        # Construct an HttpContent from a StringContent
        HttpContent _Body = new StringContent(Body);
        # and add the header to this object instance
        # optional: add a formatter option to it as well
        _Body.Headers.ContentType = new MediaTypeHeaderValue(_ContentType);
        # synchronous request without the need for .ContinueWith() or await
        response = cl.PostAsync(Uri, _Body).Result;
      }
      break;
    case "PUT":
      {
        # Construct an HttpContent from a StringContent
        HttpContent _Body = new StringContent(Body);
        # and add the header to this object instance
        # optional: add a formatter option to it as well
        _Body.Headers.ContentType = new MediaTypeHeaderValue(_ContentType);
        # synchronous request without the need for .ContinueWith() or await
        response = cl.PutAsync(Uri, _Body).Result;
      }
      break;
    case "DELETE":
      response = cl.DeleteAsync(Uri).Result;
      break;
    default:
      throw new NotImplementedException();
      break;
  }
  # either this - or check the status to retrieve more information
  response.EnsureSuccessStatusCode();
  # get the rest/content of the response in a synchronous way
  String content = response.Content.ReadAsStringAsync().Result;

  return content;
}

First thing to mention is: though “DefaultRequestHeaders” is a gettable-only property, it contains properties and methods to actually set indivisual headers.

Second, certain headers cannot be set on this collection. You have to set them on an HttpContent object when you need them and only when you may actually use them, as this is the case with “Content-Type” that cannot be used in a “GET” method. If you stil try to do it, you will get an exception like this:
“Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.”

Third, to easily work around the async behaviour (if you prefer the synchronous way) you simply use the “Result” property of the task object, instead of using “await” or “ContinueWith”. That you will have you wait until the request is finished.

And last, if you have special content types in the body of your message you can also specify this in the PostAsync/PutAsync method where you can easily do this in one of the overloads of the respective method.

Once this is in place HttpClient is just as easy to use as WebClient for example, but with tons of more features and flexibility!

4 Comments »

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.