December 17, 2012 .NET, REST, WCF 5 comments
December 17, 2012 .NET, REST, WCF 5 comments
There are few different ways of consuming REST services in .NET
In this post I would like to demonstrate how you can consume REST in C#.
Before we proceed to examples, let’s choose some very simple public REST API to consume. I stopped at this one: http://timezonedb.com/api. It is very simple service providing current local time for requested time zone. After registering you will get some key to access api. In this blog post we will use “YOUR_API_KEY”.
Here is sample response of the service we chose to consume:
<?xml version="1.0" encoding="UTF-8"?> <result> <status>OK</status> <message></message> <countryCode>AU</countryCode> <zoneName>Australia/Melbourne</zoneName> <abbreviation>EST</abbreviation> <gmtOffset>39600</gmtOffset> <dst>1</dst> <timestamp>1321217345</timestamp> </result>
Whichever approach we take, we would need some entities corresponding to the API’s response.
For XML response use cases I prepared following entity:
[XmlRoot("result")] public class TimezoneDbInfo { [XmlElement("status")] public string Status { get; set; } [XmlElement("message")] public string Message { get; set; } [XmlElement("countryCode")] public string CountryCode { get; set; } [XmlElement("zoneName")] public string ZoneName { get; set; } [XmlElement("abbreviation")] public string Abbreviation { get; set; } [XmlElement("gmtOffset")] public int GmtOffset { get; set; } [XmlElement("dst")] public int Dst { get; set; } [XmlElement("timestamp")] public int Timestamp { get; set; } }
For JSON use cases we will use the following:
[DataContract(Name = "result")] public class TimezoneDbInfoJson { [DataMember(Name = "status")] public string Status { get; set; } [DataMember(Name = "message")] public string Message { get; set; } [DataMember(Name = "countryCode")] public string CountryCode { get; set; } [DataMember(Name = "zoneName")] public string ZoneName { get; set; } [DataMember(Name = "abbreviation")] public string Abbreviation { get; set; } [DataMember(Name = "gmtOffset")] public int GmtOffset { get; set; } [DataMember(Name = "dst")] public int Dst { get; set; } [DataMember(Name = "timestamp")] public int Timestamp { get; set; } }
So let’s start!
Here is how you can consume REST using WebRequest:
var urlTemplate = "http://api.timezonedb.com/?zone={0}&key={1}"; var url = string.Format(urlTemplate, "Europe/Kiev", "YOUR_API_KEY"); var request = WebRequest.Create(url); var response = request.GetResponse(); var s = new XmlSerializer(typeof(TimezoneDbInfo)); var timezoneDbInfo = (TimezoneDbInfo)s.Deserialize(response.GetResponseStream());
And if you would need to consume same data but in JSON format, you can come up with following code:
var urlTemplate = "http://api.timezonedb.com/?zone={0}&key={1}&format=json"; var url = string.Format(urlTemplate, "Europe/Kiev", "YOUR_API_KEY"); var webClient = new WebClient(); byte[] downloadedRawResponse = webClient.DownloadData(url); var stream = new MemoryStream(downloadedRawResponse); var s = new DataContractJsonSerializer(typeof(TimezoneDbInfoJson)); var timezoneDbInfo = (TimezoneDbInfoJson)s.ReadObject(stream);
Either way, using WebRequest or WebClient allows for some lower level of flexibility like controlling timeouts, headers and many other things. For example:
Some of the disadvantages of above approach are manual building of a request URL and manual deserialization of a response. Many of you might not like the above code, thus let’s switch to WCF approach, which looks more elegant and robust at the first glance.
Not a secret that we can create REST services with WCF. Effectively this means that we can also create client for other REST services. For this all we need is just to create proxy as if it was our own service. Since, we cannot get wsdl with metadata for our service we have to create proxies manually.
To start we put some WCF configuration in place:
<system.serviceModel> <bindings> <webHttpBinding> <binding name="webHttpBindingCustom" receiveTimeout="00:01:01" sendTimeout="00:01:01"> <security mode="None"/> </binding> </webHttpBinding> </bindings> <client> <endpoint address="http://api.timezonedb.com/" binding="webHttpBinding" bindingConfiguration="webHttpBindingCustom" behaviorConfiguration="tzBehavior" contract="ConsumingRestInNet.ITimezoneDb" name="TimeZoneDbREST" /> </client> <behaviors> <endpointBehaviors> <behavior name="tzBehavior"> <webHttp/> </behavior> </endpointBehaviors> </behaviors> </system.serviceModel>
We already have some configuration flexibility which comes with WCF. Now, let’s define service contract:
[ServiceContract] public interface ITimezoneDb { [OperationContract] [XmlSerializerFormat] [WebGet(UriTemplate = "?zone={zone}&key={key}")] TimezoneDbInfo GetTimezoneInfo(string zone, string key); [OperationContract] [WebGet(UriTemplate = "?zone={zone}&key={key}&format=json")] TimezoneDbInfoJson GetTimezoneInfoJson(string zone, string key); }
(In above method we can avoid having 2 methods by orchestrating our TimezoneDbInfo entity with DataContract in addition to XmlRoot and by having 3rd param).
XML consumption:
var channelFactory = new ChannelFactory<ITimezoneDb>("TimeZoneDbREST"); var channel = channelFactory.CreateChannel(); var timezoneDbInfo = channel.GetTimezoneInfo("Europe/Kiev", "YOUR_API_KEY");
JSON consumption:
var channelFactory = new ChannelFactory<ITimezoneDb>("TimeZoneDbREST"); var channel = channelFactory.CreateChannel(); var timezoneDbInfo = channel.GetTimezoneInfoJson("Europe/Kiev", "YOUR_API_KEY");
You can also go further and implement your own Client class:
public class TimezoneDbClient : ClientBase<ITimezoneDb>, ITimezoneDb { public TimezoneDbClient(string endpointConfigurationName) : base(endpointConfigurationName) { } public TimezoneDbInfo GetTimezoneInfo(string zone, string key) { return base.Channel.GetTimezoneInfo(zone, key); } public TimezoneDbInfoJson GetTimezoneInfoJson(string zone, string key) { return base.Channel.GetTimezoneInfoJson(zone, key); } }
Now consumptions will look like piece of cake:
var client = new TimezoneDbClient("TimeZoneDbREST"); var timezoneDbInfo = client.GetTimezoneInfoJson("Europe/Kiev", "YOUR_API_KEY");
Well, of course! All nasty code went to other classes and the config.
It would be a perfect ending for a blog post. But it is not. On one hand using WCF provides us with great abstraction over service, but on the other hand it is just overhead for doing simple things. Also some people think that coupling generated by WCF defeats the point of REST.
Here is extract of a great answer on SO about what exactly is RESTful programming:
Really, what it’s about is using the true potential of HTTP. The protocol is oriented around verbs and resources. The two verbs in mainstream usage is GET and POST, which I think everyone will recognize. However, the HTTP standard defines several other such as PUT and DELETE. These verbs are then applied to resources.
To leverage all of this potential Microsoft has built System.Net.Http.HttpClient.
HttpClient is inside of namespace System.Net.Http but nuget package is called Microsoft.Net.Http. So don’t get confused. If you add the package it will simply reference System.Net.Http in your project. Nuget page says:
“This package provides a programming interface for modern HTTP applications. This package includes HttpClient for sending requests over HTTP, as well as HttpRequestMessage and HttpResponseMessage for processing HTTP messages.”
Here is how we can use HttpClient:
var urlTemplate = "http://api.timezonedb.com/?zone={0}&key={1}&format=json"; var url = string.Format(urlTemplate, "Europe/Kiev", "YOUR_API_KEY"); var httpClient = new HttpClient(); var streamTask = httpClient.GetStreamAsync(url); var s = new DataContractJsonSerializer(typeof(TimezoneDbInfoJson)); var timezoneDbInfo = (TimezoneDbInfoJson)s.ReadObject(streamTask.Result);
Code above looks pretty much the same as when we used plain WebRequest. But HttpClient is much more advanced and adopted for consuming REST services. For example, it has methods: GetAsync and PostAsync. Reading and sending custom headers is much more simplified. Here is great post with many examples of using HttpClient: http://www.bizcoder.com/index.php/2012/01/09/httpclient-it-lives-and-it-is-glorious/
So, are there any other possibilities to consume REST in .NET?
Yes. There are dozen of libraries out there implemented exactly for that, though I’m sure that a lot of them cannot be considered as good candidates.
[Edit 2015-Sep-2016] People often mention Restsharp and Hammock libraries.
Hope this post comes handy for you!