共用方式為


How to consume REST services with WCF

As you are probably aware by now, Windows Communication Foundation (WCF) 3.5 introduced a new binding called WebHttpBinding to create and to consume REST based services. If you are new to the WCF Web Programming model then see here for more details.

There have been many articles and blogs on how to host a RESTful service. However there doesn’t seem to be much written work on how to consume these services so I thought to write a few lines on this topic.

The new WebHttpBinding is used to configure endpoints that are exposed through HTTP requests instead of SOAP messages. So you can simply call into a service by using a URI. The URI usually includes segments that are converted into parameters for the service operation.

So the client of a service of this type requires 2 abilities: (1) Send an HTTP request, (2) Parse the response. The default response message format supported out of the box with the WebHttpBinding is “Plain old XML” (POX). It also supports JSON and raw binary data using the WebMessageEncodingBindingElement.

One way of consuming these services is by manually creating a HTTP request. The following example is consuming the ListInteresting operation from Flickr:

WebRequest request = WebRequest.Create("https://api.flickr.com/services/rest/?method=flickr.interestingness.getList&api_key=*&extras=");

WebResponse ws = request.GetResponse();

XmlSerializer s = new XmlSerializer(typeof(PhotoCollection));

PhotoCollection photos = (PhotoCollection)s.Deserialize(ws.GetResponseStream());

The idea is simple:

- Do the HTTP request and include all the parameters as part of the URI

- Get the response that is in XML format

- Either parse it or deserialize it into an object

The above code works but it is not elegant: We are not using the unified programming model offered by WCF and the URL is hacked together using string concatenation. The response is also manually deserialized into an object. With WCF and the WebHttpBinding we can automate most of this.

The first step is to define our service contract:

[ServiceContract]

[XmlSerializerFormat]

public interface IFlickrApi

{

  [OperationContract]

  [WebGet(

      BodyStyle = WebMessageBodyStyle.Bare,

      ResponseFormat = WebMessageFormat.Xml,

      UriTemplate = "?method=flickr.interestingness.getList&api_key={apiKey}&extras={extras}")]

  PhotoCollection ListInteresting(string apiKey, string extras);

}

As you can see, I am specifically instructing WCF to use the XML Serializer Formatter for this. The next step is to set the client endpoint. I decided to do this inside the config file:

<system.serviceModel>

  <client>

    <endpoint address="https://api.flickr.com/services/rest"

              binding="webHttpBinding"

              behaviorConfiguration="flickr"

              contract="FlickrApp.IFlickrApi"

              name="FlickrREST" />

  </client>

  <behaviors>

    <endpointBehaviors>

      <behavior name="flickr">

        <webHttp/>

      </behavior>

    </endpointBehaviors>

  </behaviors>

In order to be able to use the XML Serializer Formatter, I need XML Serializable types:

  [XmlRoot("photos")]

  public class PhotoCollection

  {

    [XmlAttribute("page")]

    public int Page { get; set; }

    ...

    [XmlElement("photo")]

    public Photo[] Photos { get; set; }

  }

  public class Photo

  {

    [XmlAttribute("id")]

    public string Id { get; set; }

    [XmlAttribute("title")]

    public string Title { get; set; }

    ...

  }

The final step is to create an instance of the client proxy:

ChannelFactory<IFlickrApi> factory =

  new ChannelFactory<IFlickrApi>("FlickrREST");

var proxy = factory.CreateChannel();

var response = proxy.ListInteresting("xxxx", "yyyy");

((IDisposable)proxy).Dispose();

If you don’t like using ChannelFactory directly then you can create your proxy by deriving from ClientBase<>:

public partial class FlickrClient :

  ClientBase<IFlickrApi>, IFlickrApi

{

  public FlickrClient()

  {

  }

  public FlickrClient(string endpointConfigurationName) :

    base(endpointConfigurationName)

  {

  }

  public FlickrClient(

    string endpointConfigurationName,

    string remoteAddress) :

    base(endpointConfigurationName, remoteAddress)

  {

  }

  public FlickrClient(string endpointConfigurationName,

    EndpointAddress remoteAddress) :

    base(endpointConfigurationName, remoteAddress)

  {

  }

  public FlickrClient(Binding binding,

    EndpointAddress remoteAddress) :

    base(binding, remoteAddress)

  {

  }

  public PhotoCollection ListInteresting(string apiKey, string extras)

  {

    return base.Channel.ListInteresting(extras);

  }

}

Now the client code will look similar to the following:

FlickrClient proxy = new FlickrClient();

var response = proxy.ListInteresting("xxxxxx","yyyyyy");

((IDisposable)proxy).Dispose();

Hope the above helps.

Comments

  • Anonymous
    April 21, 2008
    PingBack from http://microsoftnews.askpcdoc.com/?p=3505

  • Anonymous
    April 24, 2008
    Pedram, great posting.  A quick question.  Is there a way to consume a JSON based REST service? Thanks Marc

  • Anonymous
    May 02, 2008
    This is good stuff . I was going to do this myself to see how this would work ..!!

  • Anonymous
    May 16, 2008
    &#160; I thought I would do some research on REST and I think this link&#160; http://www.xfront.com/REST-Web-Services.html

  • Anonymous
    June 11, 2008
    Hi Pedram!  Thanks for the info.  There's a bug though - you have an endpointBehavior configured, but your endpoint is not referring to it.  You need to add 'behaviorConfiguration="flickr"' to the endpoint.  This is something I keep forgetting to do myself!

  • Anonymous
    June 11, 2008
    Can we invoke REST services asynchronously?

  • Anonymous
    July 09, 2008
    very good,thank you for your share

  • Anonymous
    July 09, 2008
    i have a problem when i call wcf method from extjs cross domain ,the response format can not adapt ,because it is either json or xml,neither is <script></script> format,it is perfect if wcf can response a cleartext format!

  • Anonymous
    September 18, 2008
    This was very helpful. I was using HttpWebRequest for REST and WCF for SOAP and of course it wasn't unified. I hadn't thought of doing it this way. Thanks

  • Anonymous
    October 13, 2008
    Hello! I've read you article lots of times, but I don't understand any of those app.config settings. I really would like to use REST services from Community Server. I've got the documented REST API, but I need a little help. Please check it out: http://api.communityserver.org/ I'm very interested in the blogs section. It uses headers for authentication. I can donate money if you help me. Thanks! I'll follow your blog .

  • Anonymous
    March 15, 2009
    &#160; This past Saturday was the Atlanta Code Camp.&#160; I want to be make a point of thanking Cliff

  • Anonymous
    March 18, 2009
    Ingredients: Visual Studio 2008 SP1 (C#), WCF REST Starter Kit Preview 2 In Preview 1 of the WCF REST

  • Anonymous
    August 12, 2009
    great article, very helpful - thanks!

  • Anonymous
    October 29, 2009
    I find this example somehow funny: In the first few paragraphs you are showing a perfectly fine way to interact with an HTTP server and it only takes 4 lines of code. And then you are proposing something that takes tons of additional boilerplate code, requires lots of setup and is less flexible than just consuming the XML. That makes no sense to me whatsoever.

  • Anonymous
    February 02, 2010
    That was so helpful that all you need to do now is add a printer friendly icon to your blog :D

  • Anonymous
    February 24, 2010
    Is there a way to POST XML on the request directly instead of the URI way (i mean: URL?param1=value1&param2=value2...) ?

  • Anonymous
    April 30, 2010
    how can I pass an array of objects in JSON to the service? I want to do somthing like this : I have the array of strings in JSON for example {"array":["one", "two"]} and make a POST request to the WCF service. For some kind of reason I do not understand the web method receives "array" parameter inside as null The WCF operation is defined: [OperationContract,            WebInvoke(Method = "POST",                            BodyStyle = WebMessageBodyStyle.WrappedResponse,                            RequestFormat = WebMessageFormat.Json,                            ResponseFormat = WebMessageFormat.Json)]        string DoWork(string[] array);

  • Anonymous
    June 20, 2011
    Pedram, here's is my scenario: i've my WCF Rest service solution that has ICustomer contract. i want to use that RestService in my console app (client side). As per your explanation above, do i need to add ICustomer contract  (class) again on my console app? if not, do i need to add the Rest service dll? How my console app code recognize ICustomer if i create channelfactory? Please help!

  • Anonymous
    October 20, 2011
    Hi, from the client side, how do you get the PhotoCollection type in the first place? If this was WCF, my service reference would resolve and proxy this class for me (and I'd be free to just go ahead and use it). how did you get the PhotoCollection object from the REST service before using it? XmlSerializer s = new XmlSerializer(typeof(PhotoCollection)); PhotoCollection photos = (PhotoCollection)s.Deserialize(ws.GetResponseStream());

  • Anonymous
    November 02, 2011
    So there is no way to automatically generate client proxy if server has only webHttpBinding?

  • Anonymous
    November 04, 2011
    Great post... While I'm creating my own implementation, I found it was helpful to see an example of the flickr response :   <photos page="2" pages="89" perpage="10" total="881"> <photo id="2636" owner="47058503995@N01" secret="a123456" server="2" title="test_04" ispublic="1" isfriend="0" isfamily="0" /> <photo id="2635" owner="47058503995@N01" secret="b123456" server="2" title="test_03" ispublic="0" isfriend="1" isfamily="1" /> <photo id="2633" owner="47058503995@N01" secret="c123456" server="2" title="test_01" ispublic="1" isfriend="0" isfamily="0" /> <photo id="2610" owner="12037949754@N01" secret="d123456" server="2" title="00_tall" ispublic="1" isfriend="0" isfamily="0" /> </photos>

  • Anonymous
    February 14, 2012
    The above way of conusume is really a  restbased service.. As the client call is not using Template URI of the rest service and using channelfactory , it is not a restbased service. please confirm

  • Anonymous
    January 01, 2013
    The comment has been removed

  • Anonymous
    May 21, 2013
    It would be great if you can provide code file for it.

  • Anonymous
    September 03, 2013
    Hi, Is there any way to consume the REST WCF in windows forms application ?

  • Anonymous
    January 06, 2014
    Honestly, code wise, this is one of the best examples I've seen because you are, indeed, consuming an outside service and not trying to build it inside the app. That being said, what is missing from this example is several key screen shots on WHERE you are sticking your code inside the MVC api. This is not intuitive to beginners like me, and anyone familiar with the Visual Studio ASP.NET MVC environment knows there are multiple areas in the structure in which code is nested. This is why, as straightforward as your code seems, it is still not very helpful, and yet it really is one of the best examples I have found. Chewbacca! movie-sounds.net/.../62

  • Anonymous
    February 27, 2014
    Nice Article

  • Anonymous
    September 23, 2014
    I agree with Jessica. There are several missing pieces to make this work. First of all how would the client application know that is the ServiceContract to be used in the Channel factory. I mean, since for the webHTTP binding SOAP header is not available.? Do i need to make direct reference? Even if so, after trying I dont see the channelfactory able to recognize my webhttp service hosted elsewhere. Request you to put screenshots of the all the pieces of code, if possible a sample app.