WCF: Consume WCF REST service by HttpClient
In a recent case, one of my customers requested how to consume the WCF REST Service by taking the help of System.Net.Http.HttpClient modules (introduced in .NET 4.5). I hope the following details would help in depth.
Create a new WCF service application project named "RestService"
- WCF REST service contract appears as the following.
namespace RestService
{
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebInvoke(Method ="GET", ResponseFormat = WebMessageFormat.Json,
UriTemplate = "get/{value}")]
string GetData(string value);
[OperationContract]
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json,
UriTemplate = "get/add")]
string AddData(int value);
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json,
UriTemplate = "GetCustomer/{customerId}")]
Customer GetCustomer(string customerId);
[OperationContract]
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json,
UriTemplate = "UpdateCustomer")]
List<Customer> UpdateCustomer(Customer customer);
}
[DataContract]
public class Customer
{
[DataMember]
public string Id { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
[DataMember]
public string SSN { get; set; }
}
}
- The WCF REST service implementation appears as the following.
namespace RestService
{
public class Service1 : IService1
{
public static List<Customer> customerList = null;
public Service1()
{
customerList = new List<Customer>();
// Currently, it is statically loaded with dummy values.
// However, it can be extended to populate from a data source (e.g. SQL).
customerList.Add(new Customer
{
Id = "1111",
FirstName = "John",
LastName = "Doe",
SSN = "451234590"
});
customerList.Add(new Customer
{
Id = "1112",
FirstName = "Jason",
LastName = "Rudd",
SSN = "451234592"
});
customerList.Add(new Customer
{
Id = "1113",
FirstName = "Daniel",
LastName = "Cheng",
SSN = "451234596"
});
}
public string GetData(string value)
{
return string.Format("You entered: {0}", value);
}
public string AddData(int value)
{
return string.Format("You entered: {0}", value);
}
public Customer GetCustomer(string customerId)
{
//TODO: query your repo and pull customer details
return customerList.Find(t => t.Id == customerId);
}
public List<Customer> UpdateCustomer(Customer customer)
{
//TODO: update customer information in your repo
var targetCustomer = customerList.Where(t => t.Id == customer.Id).FirstOrDefault();
if (targetCustomer != null)
{
// no need to update cutomerId
targetCustomer.FirstName = customer.FirstName;
targetCustomer.LastName = customer.LastName;
targetCustomer.SSN = customer.SSN;
}
return customerList;
}
}
}
- Web.config should be updated like the following for WCF REST service hosting on IIS.
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
<endpointBehaviors>
<behavior name="ep">
<webHttp helpEnabled="true"/>
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service name="RestService.Service1">
<endpoint address=""
binding="webHttpBinding" bindingConfiguration="webh" behaviorConfiguration="ep" contract="RestService.IService1"/>
</service>
</services>
<bindings>
<!-- The following customBinding can be used if intention is to convert "webHttpBinding" to "customBinding" for some reason. -->
<customBinding>
<binding name="webHttpDeviceBinding">
<webMessageEncoding>
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
</webMessageEncoding>
<httpTransport manualAddressing="true" maxBufferPoolSize="2147483647"
maxReceivedMessageSize="2147483647" authenticationScheme="None"
maxBufferSize="2147483647" maxPendingAccepts="15" transferMode="Buffered" />
</binding>
<webHttpBinding>
<binding name="webh">
<security mode="None" />
</binding>
<binding name="webHttpDeviceBinding" closeTimeout="00:10:00"
openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00"
maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647"
transferMode="Buffered">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<security mode="None" />
</binding>
</webHttpBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
Host the WCF REST service application on IIS
- Create an application pool named "RestServicePool"
- Right click on VS project "RestService" -> Go to "Web" section -> set server to "Local IIS"/ prepare your path and "Create Virtual Directory"
- Application would be deployed on IIS
- Select the application on IIS manager -> Go to "Advanced Settings" -> Set the application pool to your pool "RestServicePool"
Check if you are able to read "metadata" of the WCF REST service
- Browse the help URL (e.g. <pupanda-win10.fareast.corp.microsoft.com/RestService/Service1.svc/help>)
- Screenshot for reference
Consume the REST service by HttpClient
- Create a console application named "RestClient"
- "Add Service Reference" to your service URL <pupanda-win10.fareast.corp.microsoft.com/RestService/Service1.svc> [reason to go for the plan of downloading proxy classes w.r.t. service classes, is to ensure that custom classes can smoothly be "de-serialized" or "serialized"]
- Go to your "Program.cs", update the definition with the following code snippet
private static async Task PostResult()
{
HttpResponseMessage response = null;
using (var client = new HttpClient())
{
var uri = new Uri("pupanda-win10.fareast.corp.microsoft.com/RestService/Service1.svc/get/add");
var jsonRequest = JsonConvert.SerializeObject("123");
var stringContent = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
response = await client.PostAsync(uri, stringContent);
}
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
private static async Task GetResult()
{
string response = null;
using (var client = new HttpClient())
{
var uri = new Uri("pupanda-win10.fareast.corp.microsoft.com/RestService/Service1.svc/get/123");
response = await client.GetStringAsync(uri);
}
Console.WriteLine(response);
}
private static async Task GetCustomerResult()
{
string response = null;
using (var client = new HttpClient())
{
var uri = new Uri("pupanda-win10.fareast.corp.microsoft.com/RestService/Service1.svc/GetCustomer/1113");
response = await client.GetStringAsync(uri);
}
Console.WriteLine(response);
}
private static async Task PostCustomerResult()
{
HttpResponseMessage response = null;
using (var client = new HttpClient())
{
var uri = new Uri("pupanda-win10.fareast.corp.microsoft.com/RestService/Service1.svc/UpdateCustomer");
Customer toUpdateCustomer = new Customer {
Id = "1113",
FirstName = "Daniel",
LastName = "Gates",
SSN = "4512xx596"
};
var jsonRequest = JsonConvert.SerializeObject(toUpdateCustomer);
var stringContent = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
response = await client.PostAsync(uri, stringContent);
}
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
static void Main(string[] args)
{
Console.WriteLine("Press 1 for GET String/ Press 2 for POST String/ Press 3 for GET Customer/ Press 4 for POST Customer");
var input = Console.ReadLine();
switch (input)
{
case "1":
Console.Write("Results from GET operation: ");
Task.Run(() => GetResult());
break;
case "2":
Console.Write("Results from POST operation: ");
Task.Run(() => PostResult());
break;
case "3":
Console.Write("Results from GET Customer operation: ");
Task.Run(() => GetCustomerResult());
break;
case "4":
Console.Write("Results from POST Customer operation: ");
Task.Run(() => PostCustomerResult());
break;
default:
Console.WriteLine("wrong option");
break;
}
Console.ReadLine();
}
Self-host the WCF REST Service
- Create a console application named "RestServiceSelfHost"
- Add reference to the "RestService" project
- Update the "App.config" with following content
<system.serviceModel>
<services>
<service name="RestService.Service1">
<host>
<baseAddresses>
<add baseAddress="localhost:8080/myrestservice"/>
</baseAddresses>
</host>
<endpoint address=""
binding="webHttpBinding"
behaviorConfiguration="epB"
contract="RestService.IService1"/>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="epB">
<webHttp helpEnabled="true"/>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
- Update the Program.cs with following content
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(Service1)))
{
host.Open();
Console.WriteLine("The service is ready at {0}", host.Description.Endpoints[0].Address);
Console.WriteLine("Press to stop the service.");
Console.ReadLine();
// Close the ServiceHost.
//host.Close();
}
}
Consume the self-hosted WCF REST service by HttpClient
- Create a console application named "RestServiceSelfHostClient"
- Update your "Program.cs" with the following section of code
static void Main(string[] args)
{
Console.Write("Results from GET operation: ");
Task.Run(() => GetResult());
Console.ReadLine();
}
private static async Task GetResult()
{
string response = null;
using (var client = new HttpClient())
{
var uri = new Uri("localhost:8080/myrestservice/get/123");
response = await client.GetStringAsync(uri);
}
Console.WriteLine(response);
}
Note
I have uploaded the whole sample application for your reference in OneDrive. It is a one solution having all the above details.