Posted by: dotnetninja | May 2, 2008

REST Service with WCF and JSON

UPDATE: I would not advise to use WCF for building RESTful endpoints. This post shows how it could be done with WCF ( .Net 3.5). There are plenty rest libraries that can be used. If you prefer one developed by Microsoft you can use ASP.Net Web Api. I would also recommend book REST in Practice which explains what REST is all about and what is not is only exposing http endpoints in CRUD style.

What is REST?

Here are definitions:

“Representational State Transfer is intended to evoke an image of how a well-designed Web application behaves: a network of web pages (a virtual state-machine), where the user progresses through an application by selecting links (state transitions), resulting in the next page (representing the next state of the application) being transferred to the user and rendered for their use.”

Principles (taken from Wikipedia):

“REST’s proponents argue that the web has enjoyed the scalability and growth that it has as a result of a few key design principles:

Application state and functionality is divided into resources

Every resource is uniquely addressable using a universal syntax for use in hypermedia links

  • All resources share a uniform interface for the transfer of state between client and resource, consisting ofA constrained set of well-defined operations

    A constrained set of content types, optionally supporting code-on-demand

A protocol that is:
Client/Server
Stateless
Cacheable
Layered”

This is a contrast to a RPC (Remote Procedure Call) model, which is commonly implemented using SOAP or the simpler XML-RPC. There, you expose a range of methods or procedures that can be called and supply them with data to operate on. The focus here is on verbs – the operation that is being performed.

Let’s build a very simple REST-full WCF service. I am going to use Visual Studio 2008 and for testing service Fiddler.

Open Visual Studio and create new WCF service library.

Change the name of the IService1 interface to IHelloRest and class Service1 to HelloRestService.

Add reference System.ServiceModel.Web to this project.

Remove all operations from the IHelloRest interface and add first test operation. Observe here WebGet attribute which allows clients to use your services using HTTP GET attribute. Also observe message format – JSON and UriTemplate.
IHelloRest.cs:

[System.ServiceModel.ServiceContract]
    public interface IHelloRest
    {
        [System.ServiceModel.OperationContract]
        [System.ServiceModel.Web.WebGet(UriTemplate = "helloto/{name}", ResponseFormat = System.ServiceModel.Web.WebMessageFormat.Json)]
        string Hello(string name);
        [System.ServiceModel.OperationContract]
        [System.ServiceModel.Web.WebGet(UriTemplate = "isalive/{animal}", ResponseFormat = System.ServiceModel.Web.WebMessageFormat.Json)]
        Animal CheckIfAlive(string animal);

        [System.ServiceModel.OperationContract]
        [System.ServiceModel.Web.WebInvoke(UriTemplate = "animals", ResponseFormat = System.ServiceModel.Web.WebMessageFormat.Xml)]
        Animal[] PostAnimal(Animal animal);

        [System.ServiceModel.OperationContract]
        [System.ServiceModel.Web.WebGet(UriTemplate = "gallery/{pictureId}")]
        System.IO.Stream GetPictureThumbnail(string pictureId);

    }
    [System.Runtime.Serialization.DataContract]
    public class Animal
    {
        [System.Runtime.Serialization.DataMember]
        public bool IsAlive { get; set; }
        [System.Runtime.Serialization.DataMember]
        public string Name { get; set; }
    }

And HelloRestService.cs

public class HelloRestService : IHelloRest
    {
        #region IHelloRest Members

        public string Hello(string name)
        {
            return String.Format("Hello:{0}", name);
        }
        public Animal CheckIfAlive(string name)
        {
            return new Animal { IsAlive = true, Name = name };
        }
        public Animal[] PostAnimal(Animal animal)
        {
            List<Animal> a = new List<Animal>();
            a.Add(new Animal { IsAlive = false, Name = "mamut" });
            a.Add(new Animal { IsAlive = true, Name = "dog" });
            a.Add(new Animal { IsAlive = true, Name = "ape" });
            return a.ToArray();
        }
        public System.IO.Stream GetPictureThumbnail(string pictureId)
        {
            System.IO.Stream stream = System.IO.File.Open(@"ow.jpg",System.IO.FileMode.Open);
            // set the Content-Type to image/jpeg
            System.ServiceModel.Web.WebOperationContext.Current.OutgoingResponse.ContentType = "image/jpeg";
            return stream;
        }
        #endregion
    }

Now add new Console Project to the same solution. Add also application configuration file to this project and insert.

Add System.ServiceModel, System.ServiceModel.Web and HelloRest(WCF Library) reference to console project.

Here is my app.config:


<configuration>
  <system.serviceModel>
    <bindings>
    </bindings>
    <services>
      <service name="HelloRest.HelloRestService" behaviorconfiguration="Default">
        <host>
          <baseAddresses>
            <add baseaddress="http://localhost:8081/json"></add>
          </baseAddresses>
        </host>
        <endpoint address="" binding="webHttpBinding" contract="HelloRest.IHelloRest"></endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="Default">
          <serviceMetadata httpgetenabled="true"></serviceMetadata>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

And here is Program.cs:

class Program
    {
        static void Main(string[] args)
        {
            using (WebServiceHost serviceHost = new WebServiceHost(typeof(HelloRest.HelloRestService)))
            {
                serviceHost.Open();
                Console.WriteLine("WCF Service is running...");
                Console.ReadLine();
                serviceHost.Close();
            }
            Console.WriteLine("WCF Service has closed");
        }
    }

Now start console application and your service should be running.

Now we can use fiddler to test our service:

Open fiddler and type in the request builder url with inparameter({name} defined in the interface) to your REST service.

You should get something like this:

Now if you click on result to the left and open Session View you should see this:

Your Rest service worksJ

Now test other operations returning complex types:

If you test http://localhost:8081/json/isalive/dog, you should get animal object in JSON like this:

{"IsAlive":true,"Name":"dog"}

Now let's try to get an image with GetPictureThumbnail
operation. Add an image to the console project and under properties in the project for the image change "Copy to Output Directory" to copy if newer.

You should also change in the code for the operation to the name of your image.

Write http://localhost:8081/json/gallery/1 in the fiddler or the browser and you should get back image. This operation could be used by passing picture id and returning thumbnail when generating gallery.

At the end we will test post with PostAnimal operation. In the Request Builder (Fiddler) write in the Request Header and change Get TO POST:

User-Agent: Fiddler

Host: localhost:8081

Content-Type: application/json; charset=utf-8

Content-Length: 0

See image:

Result should be something like this:

That was simple……


Responses

  1. Aman,

    I don’t seem to be able to figure it out how to pass objects to a method call, as in your PostAnimal method;

    [System.ServiceModel.OperationContract]
    [System.ServiceModel.Web.WebInvoke(UriTemplate = "animals", ResponseFormat = System.ServiceModel.Web.WebMessageFormat.Xml)]
    Animal[] PostAnimal(Animal animal);

    An ‘Animal’ is a parameter of the method but you do not pass it to the method call when testing it in the Fiddler. Why?

    Could you show me how to do it.

    Thanks,
    Roman

  2. Hi Roman
    Sorry for late answer, I got back from vacation yesterday :). Here is how you could do.

    1. In IHelloRest.cs change Animal class like this:
    [System.Runtime.Serialization.DataContract(Name = "Animal",Namespace="")]
    public class Animal

    2. In HelloRestService.cs change PostAnimal like this:

    public Animal[] PostAnimal(Animal animal)
    {
    List a = new List();
    //a.Add(new Animal { IsAlive = false, Name = “mamut” });
    //a.Add(new Animal { IsAlive = true, Name = “dog” });
    //a.Add(new Animal { IsAlive = true, Name = “ape” });
    a.Add(animal);
    return a.ToArray();
    }

    3. Start Service and start fiddler

    4. Navigate to http://localhost:8081/json/animals in request builder with POST

    5. Add this to Request header
    User-Agent: Fiddler
    Host: localhost:8081
    Content-Type: application/xml; charset=utf-8
    Content-Length: 57

    and

    this in Request Body

    <Animal><IsAlive>true</IsAlive><Name>arman</Name></Animal>
  3. This post has been very helpful for me, one question though. Building on your last answer… what if Animal included a custom enumeration? I can’t seem to get that to work with PostAnimal.

    Thanks,
    James

  4. I figured it out… I had to remove the DataContract attribute from the enum.

    -James

  5. Hi

    I’m glad that I could help you and great about finding solution with enum :)

  6. Hey,

    What happens to the stream handle after it is returned?

    There is no explicit location where you can Close the stream.

    Using the “using” statement also returns an empty file.

    Do you know what’s going on behind the scenes?

    • Investigating…

      Check out WCF Starter Kit with AdapterStream there is an example there using AdapterStream (PushStyleStreaming)

  7. Thank you! This helped me tremendously!

  8. Nice Post. I have different kind of scenario now.. Instead of service library, i took service application of Framework4 and i wrote web.config same as App.config mentioned in this site…! Now, when i try to host on IIS 7 (windows 7) i land up in couple of errors and service doesnt run.. these error keeps on recycling as one goes and other previous one comes again..

    Now Error ..!!

    Server Error in ‘/HelloRest/HelloRestService.svc’ Application.
    The resource cannot be found.
    Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.

    Requested URL: /HelloRest/HelloRestService.svc
    ——————————————————————————–
    Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.1

    THIS IS MY URL IN WEBCONFIG http://localhost/HelloRest/HelloRestService.svc

    Heres my Web.config

    I ALSO TRIED ServiceModelReg.exe -iru and -i and aspnet_regiis.exe etc.. none of them help me towards my goal !!!

    PLEASE HELP AS AM IN BIG PROBLEM AND CANT PROCEED ………

    • I am in real need today and that too very urgent.

      I have gone through your tutorial again, and found that you have implemented the WebInvoke for that Animal[] example.
      This is with xml,

      Now, I observed that you have implemented it with Content-Length : 0, i.e. you don’t have a request body associated with it.

      But in my case am trying to pass a Jsonstring as request body and it fails. Ofcourse the attributes are WebMessageFormat.Json but still it does not work and my fiddler shows this error.

      HTTP/1.1 400 Bad Request

      I have also emailed you my query.

    • I have the same problem… Just I see the svc and I can not access with “json/isalive/dog after .svc”

  9. Great post! Despite being an old post this helped me get up and running with writing my own REST service in no time with VS2010. Helped me tremendously in understanding how WebGet works.

  10. Awesome post! Helped me big time!
    But what about posting json (instead of xml) as an Animal? Is that possible?

    “Animal”:{“IsAlive”:”true”,”Name”:”blabla”} instead of
    truearman

    Thanks in advance ;)

  11. I want to remark that I had to configure a namespace reservation for running the server on windows 7, see http://msdn.microsoft.com/en-us/library/ms733768.aspx
    I had to enable the port 8081 used here for the current user. This worked for me: Open a console and type
    netsh http add urlacl url=http://+:8081/json user=\

  12. Hi ,

    I can see the service on WCF Cliente but when I try to use the url I see just the svc as any wcf project.

    How I could publish this on IIS To see the JSON encoding?

  13. Hi,
    first I want to say – great post! It was very helpful :)
    But I have some troubles. When I start console application, my service is not running. It says that the error is in this line: “using (WebServiceHost serviceHost = new WebServiceHost(typeof(HelloRest.HelloRestService)))” and the message is: “Unrecognized attribute ‘behaviorconfiguration’. Note that attribute names are case-sensitive. (C:\Users\Daria\Documents\Visual Studio2008\Projects\HelloRest\RestConsole\bin\Debug\RestConsole.vshost.exe.Config line 7)”.
    Do you have any idea what could be the problem?
    Thank you!

  14. Hi,
    Iam developed employee management system by using webservice(In solution Explorer i taken 4projects and adding reference of each other).Now.I have to develop REST Web Service for above same project.But In REST How i get HTTP Object..

  15. I am also creating one wcf rest service ,but it’s not working What is the problem

    Service1.cs

    public class Service1 : IService1
    {

    public List GetTagXML()
    {
    return Getagdata();
    }
    private List Getagdata()
    {
    List Tagdata = new List
    {
    new Tagdata
    {
    Tagid=”111″
    },
    new Tagdata
    {
    Tagid=”222″
    },

    };
    return Tagdata;

    }
    }
    IService.cs

    public interface IService1
    {

    [OperationContract]
    [WebGet(UriTemplate = "/XMLGetData",
    RequestFormat = WebMessageFormat.Json,
    ResponseFormat = WebMessageFormat.Json,
    BodyStyle = WebMessageBodyStyle.Bare)]

    //string XMLGetData(String Tagid);
    List GetTagXML();

    }

    creating this service
    I am running this servoce

    http://127.0.0.1:81/Service1.svc/GetTagXML
    Not shoing the data in JSON format plz rectify my problem

  16. Right here is the perfect site for anybody who wishes to find out about this topic. You realize so much its almost tough to argue with you (not that I really would want to…HaHa). You definitely put a fresh spin on a topic that’s been written about for a long time. Wonderful stuff, just great!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: