Continue from the previous post, I would like to share how to make HTTP GET call to a WCF REST Service.
Following is my operation contract setup:
Well, this code cannot work with the WCF REST service where the operation contract is decorated with WebGet attribute. The reason is WebGet attribute actually make the data retrieval operation expect a GET method call. But, the above code is actually making a POST method call. See the following RAW data which is made by the proxy class:
You would get the response HTTP 405 - Method Not Allowed.
Therefore, we have to use WebChannelFactory to make the WCF REST service call with HTTP GET method. Here is the code:
And, here is the HTTP RAW content made by the WebChannelFactory:
There is another way to call the WCF REST service which is by using HttpClient. Here is the code:
See the following RAW content made by the HttpClient:
Simple and let's look at the result. Note: There is a tricky part when deal with WCF REST service return result. Look at the following RAW content return from the service:
The List<Result> type has been serialized into JSON as you can see above, however, it is assigned to one property call ListResultsResult as highlighted above. Therefore, in my code with HttpClient, after deserialize the whole JSON string into a JObject (from Newtonsoft), I have to have this code jObj.GetValue("ListResultsResult") to get the real result return by the service method. And then, since the data type is a generic list, it must be a JArray type. I have to convert the result into JArray first, then only convert it into List<Result> type.
Compare to WCF client that use WebChannelFactory, the serialization and deserialization is done at the back with the .net serialization library. It is transparent to you with this one line of code: List<LayeredWebApi.Entities.Result> results = channel.ListResults();
Well, it is up to your call which WCF REST client you wish to use.
Following is my operation contract setup:
[OperationContract]
[WebGet(UriTemplate = "ListResults",
ResponseFormat = WebMessageFormat.Json)]
List<Result>
ListResults();
WCF Service Client
Normally, when we want to call that web service, we can simply perform the "Add Service Reference" from the Visual Studio, and the WCF client aka the proxy class will be auto-generated. Therefore, the following is the code that you normally would use to call a WCF service:
ResultServiceClient proxy = new ResultServiceClient();
List<Result>
results = proxy.ListResults();
Well, this code cannot work with the WCF REST service where the operation contract is decorated with WebGet attribute. The reason is WebGet attribute actually make the data retrieval operation expect a GET method call. But, the above code is actually making a POST method call. See the following RAW data which is made by the proxy class:
POST
http://127.0.0.1:65000/ResultService.svc/ListResults HTTP/1.1
Content-Type: application/xml; charset=utf-8
VsDebuggerCausalityData: uIDPo8z4w4PqwblMjjlSjLko010AAAAA6fc873e/5U+GAxRCKLz7Mps+z0ILaFhMs1wQZ9g/XOwACQAA
E2EActivity: DUiXbbtRBk6NgLuSr8X7+A==
Host: 127.0.0.1:65000
Content-Length: 42
Expect: 100-continue
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
<ListResults xmlns="http://tempuri.org/"/>
You would get the response HTTP 405 - Method Not Allowed.
HTTP/1.1 405 Method Not Allowed
Server: ASP.NET Development Server/11.0.0.0
Date: Fri, 17 May 2013 00:40:32 GMT
X-AspNet-Version: 4.0.30319
Allow: GET
Content-Length: 1565
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Connection: Close
Therefore, we have to use WebChannelFactory to make the WCF REST service call with HTTP GET method. Here is the code:
var behavior = new WebHttpBehavior();
behavior.DefaultBodyStyle
= WebMessageBodyStyle.Wrapped;
//Note:
the IResultService is not the one generated from the svcutil.exe
//It
should be your Service Contract
using (var factory = new WebChannelFactory<LayeredWebApi.Services.Contracts.IResultService>(
new WebHttpBinding(),
new Uri("http://ipv4.fiddler:65000/ResultService.svc")
))
{
factory.Endpoint.EndpointBehaviors.Add(behavior);
var channel = factory.CreateChannel();
//Note:
the Result object is not the one generated from the svcutil.exe
List<LayeredWebApi.Entities.Result> results = channel.ListResults();
}
And, here is the HTTP RAW content made by the WebChannelFactory:
GET
http://127.0.0.1:65000/ResultService.svc/ListResults HTTP/1.1
Content-Type: application/xml; charset=utf-8
VsDebuggerCausalityData:
uIDPo6WsSaH5x2NJta5xXQVoGTcAAAAA5M7pLj2DXEOHIOoTRG4R1t5jyJvtJCVLlnuRlW/5RqcACQAA
E2EActivity: BOiPxzfbnUCLg4OkbNlTDw==
Host: 127.0.0.1:65000
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
HttpClient
There is another way to call the WCF REST service which is by using HttpClient. Here is the code:
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("http://ipv4.fiddler:65000");
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("ResultService.svc/ListResults").Result;
Assert.IsTrue(response.IsSuccessStatusCode,
"Failed to call WCF service.");
string json = response.Content.ReadAsStringAsync().Result;
JObject jObj = JsonConvert.DeserializeObject(json) as JObject;
//Here
is the tricky part.
//WCF
REST service return the real result object inside an object property
//The
object property name is prefixed with service: e.g. <ServiceName>Result
//You
know that your result is a generic list, you have to convert the result to
JArray first
//Then
convert the JArray to generic List
List<Result> result = jObj.GetValue("ListResultsResult")
.ToObject<JArray>()
.ToObject<List<Result>>();
}
See the following RAW content made by the HttpClient:
GET
http://127.0.0.1:65000/ResultService.svc/ListResults HTTP/1.1
Accept: application/json
Host: 127.0.0.1:65000
Connection: Keep-Alive
Simple and let's look at the result. Note: There is a tricky part when deal with WCF REST service return result. Look at the following RAW content return from the service:
HTTP/1.1 200 OK
Server: ASP.NET Development Server/11.0.0.0
Date: Fri, 17 May 2013 03:35:08 GMT
X-AspNet-Version: 4.0.30319
Content-Length: 286
Cache-Control: private
Content-Type: application/json; charset=utf-8
Connection: Close
{"ListResultsResult":[{"<ID>k__BackingField":1,"<Name>k__BackingField":"Ah
Beng","<Score>k__BackingField":50},{"<ID>k__BackingField":2,"<Name>k__BackingField":"Ah
Lian","<Score>k__BackingField":72},{"<ID>k__BackingField":3,"<Name>k__BackingField":"Ah
Boon","<Score>k__BackingField":1}]}
The List<Result> type has been serialized into JSON as you can see above, however, it is assigned to one property call ListResultsResult as highlighted above. Therefore, in my code with HttpClient, after deserialize the whole JSON string into a JObject (from Newtonsoft), I have to have this code jObj.GetValue("ListResultsResult") to get the real result return by the service method. And then, since the data type is a generic list, it must be a JArray type. I have to convert the result into JArray first, then only convert it into List<Result> type.
Compare to WCF client that use WebChannelFactory, the serialization and deserialization is done at the back with the .net serialization library. It is transparent to you with this one line of code: List<LayeredWebApi.Entities.Result> results = channel.ListResults();
Well, it is up to your call which WCF REST client you wish to use.
No comments:
Post a Comment