Wednesday 9 March 2011

How to create JSON WCF RESTful Service in 60 seconds

WCF makes it very easy to expose JSON data over a RESTful interface, as long as you are aware of a couple of “gotchas” in advance.

This article will explain those to you, so you can focus on your business logic rather than configuration of your WCF services.

We start this example by creating a WCF Service Library project:

new-project

Next we need to add a reference to the System.ServiceModel.Web framework.  Right click on your project file and select Add Reference…

add-reference

As this framework is not part of the .Net Framework 4 Client Profile, Visual Studio kindly informs us that it will update our target Framework to the full version of .Net Framework 4.  Click Yes to accept this change:

target-change

We are now ready to update our code.

Copy and paste the following code into the App.Config file:

<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<services>
<service name="WcfJsonRestService.Service1">
<endpoint address="http://localhost:8732/service1"
binding="webHttpBinding"
contract="WcfJsonRestService.IService1"/>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior>
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>


Notice the binding is set to webHttpBinding as opposed to the normal project template default of wsHttpBinding. 


The other important change is the addition of an endpointBehavior for WebHttp.


These two changes are required to enable JSON over REST for WCF.


Copy and paste the following code into the IService1 file:

using System.ServiceModel;

namespace WcfJsonRestService
{
[ServiceContract]
public interface IService1
{
[OperationContract]
Person GetData(string id);
}
}


Notice we are accepting an “In” parameter for id of datatype string.  For this example we are returning a custom type of Person.




Copy and paste the following code into the Service1.cs file:

using System;
using System.ServiceModel.Web;

namespace WcfJsonRestService
{
public class Service1 : IService1
{
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "data/{id}")]
public Person GetData(string id)
{
// lookup person with the requested id
return new Person()
{
Id = Convert.ToInt32(id),
Name = "Leo Messi"
};
}
}

public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
}


The key elements here are the attributes applied to the method.  We are enabling the method to be called over HTTP GET, returning the data in Json format and setting the Uri template to ensure we are using a RESTful interface.


To test your brand new service we will pass in the id value of 10 simply by opening your favourite browser and pasting in the following URL: 


http://localhost:8732/Service1/data/10


json-browser


Source code:  WcfJsonRestService.zip


Example With Multiple Parameters

using System;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace WcfJsonRestService
{
[ServiceContract]
public interface IService1
{
[OperationContract]
Person GetData(string id);

[OperationContract]
Person GetDataWithTwoParams(string id, string name);
}

public class Service1 : IService1
{
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "data/{id}")]
public Person GetData(string id)
{
// lookup person with the requested id
return new Person()
{
Id = Convert.ToInt32(id),
Name = "Leo Messi"
};
}

[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "data/{id}/{name}")]
public Person GetDataWithTwoParams(string id, string name)
{
// create person with the requested id and name
return new Person()
{
Id = Convert.ToInt32(id),
Name = name
};
}
}

public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
}

31 comments:

  1. great article,
    but I have some problem,
    the error message is "wcf service host cannot find any service metadata". How to solve it???

    ReplyDelete
  2. Hi Rio, the service metadata is only required if an external client wants to interrogate your service. What's happening on your machine is that Visual Studio is attempting to start up the default WcfTestClient tool, which cannot find the metadata (as we have not exposed any). That's fine because we are accessing the service from our browser which doesn't require the metadata. Hope this makes sense, if not let me know and I'll happily share a Skype call to explain.

    ReplyDelete
  3. To turn off the WcfTestClient tool, right click on your project > Properties > Debug then remove the /client:"WcfTestClient.exe" text from the command line arguments.

    ReplyDelete
  4. When I run the application I get the open / save file download dialog ?

    ReplyDelete
  5. Hi Goit, the link is for a download of the C# source code. You'd need to save the zip file to your local machine unzip it and open the solution or project file in Visual Studio 2010.

    ReplyDelete
  6. Sorry, my fault, I didnt explain that very well...

    The problem was after I compiled the application and ran it I attempted to browse to http://localhost:8732/Service1/data/10 and then got the open / save file prompt.

    I have since found that this would be the standard behaviour in browsers for the json content type.

    Great post. Thanks

    ReplyDelete
  7. Great Post. But on Windows Vista - you may need to first enable your process to listen on port 8732. Otherwise you will get an exception error. So do the following command where you replace Sasha-PC by your domain and Sasha by your user name:
    netsh http add urlacl url=http://+:8732/service1 user=Sasha-PC\Sasha

    Aleksandr Yampolskiy (http://yampolskiy.blogspot.com)

    ReplyDelete
  8. Found your posting and liked it very much since I´m a bit new to WCF, it shows some simplicity on creating embedded JSON services. I am running into some problems with security. When I run this as a normal user without elevated trust on the actual computer, it throws an exception when setting up the HttpListener.

    System.Net.HttpListenerException (0x80004005):

    What would the workaround be in a normal application?

    ReplyDelete
  9. Nice post, how can i access get method with multiple parameters

    ReplyDelete
  10. Uday, give this a go and let me know how you get on:


    using System;
    using System.ServiceModel;
    using System.ServiceModel.Web;

    namespace WcfJsonRestService
    {
    [ServiceContract]
    public interface IService1
    {
    [OperationContract]
    Person GetData(string id);

    [OperationContract]
    Person GetDataWithTwoParams(string id, string name);
    }

    public class Service1 : IService1
    {
    [WebInvoke(Method = "GET",
    ResponseFormat = WebMessageFormat.Json,
    UriTemplate = "data/{id}")]
    public Person GetData(string id)
    {
    // lookup person with the requested id
    return new Person()
    {
    Id = Convert.ToInt32(id),
    Name = "Leo Messi"
    };
    }

    [WebInvoke(Method = "GET",
    ResponseFormat = WebMessageFormat.Json,
    UriTemplate = "data/{id}/{name}")]
    public Person GetDataWithTwoParams(string id, string name)
    {
    // create person with the requested id and name
    return new Person()
    {
    Id = Convert.ToInt32(id),
    Name = name
    };
    }
    }

    public class Person
    {
    public int Id { get; set; }
    public string Name { get; set; }
    }
    }

    ReplyDelete
  11. Uday, you call the new service like this:

    http://localhost:8732/Service1/data/99/udaykotta

    which would return you a person object with Id = 99 and Name = udaykotta

    Hope this helps you out, Steve

    ReplyDelete
  12. Hi there Steven,

    I've gotten the code to run and working on my local machine, but I'm running into an issue deploying it.

    How do I add metadata to this project and how would I go about deploying a service like this on a live server? I've checked around a lot and haven't had any success deploying the service aside from getting it to run on localhost:8732/Service1

    I've made sure to change the endpoint address to where it will reside on the deployment server, and I can get to the service page when I browse to the .svc file, but that file is returning a metadata error. Any advice would be great.

    Thank you.

    ReplyDelete
    Replies
    1. Hi JKMach, to add a mex metadata endpoint use the config in the following link. Check this works locally for you first then try on the server. If you're still having problems let me know what error you're having on the server.

      https://gist.github.com/2783923

      Delete
    2. I still must be missing something. I've gotten it to load up on my localhost, and I get the metadata loading (thank you, even gives me the wsdl output). But when I try to type in http://localhost/Service1/data/10 It tells me that it is getting File or directory not found. (I've created the folder as an application in IIS already).

      Delete
    3. I have changed this line in the app.config to read: add baseAddress="http://localhost/Service1" as well.

      Delete
    4. Please see instructions below.

      Bear in mind Microsoft also now have http://www.asp.net/web-api

      Have a great weekend!

      Delete
  13. Hi JKMach

    For deployment you'll need to configure your machine first. Assuming Windows 7 64 bit (if you're running a different system and can't follow my instructions then please let me know). I've included some screenshots which hopefully will make it easier to follow:

    1) Install WCF Activation:

    Start > Control Panel > Programs and Features > Turn Windows Features On or Off > Microsoft .NET Framework 3.5.1 > (CHECK) Windows Communication Foundation HTTP Activation

    http://stevenhollidge.com/blog-source-code/wcf/1.PNG

    2) Configure ASP.NET by opening a command prompt as administrator:

    CD C:\Windows\Microsoft.NET\Framework64\v4.0.30319
    aspnet_regiis -i

    Full instructions here: http://msdn.microsoft.com/en-us/library/k6h9cz8h.aspx

    http://stevenhollidge.com/blog-source-code/wcf/2.PNG

    Now your machine is configured you need to deploy your service. First we'll deploy using a svc file:

    1) Open Visual Studio using "Run as Administrator" (you can do this by holding SHIFT and right clicking on the Visual Studio shortcut and select "Run as Administrator")

    2) Right click on the "WcfJsonRestService" project file and select "Publish..."

    3)
    a) Target location: http://localhost/service1
    b) Select "delete all existing files prior to publish
    c) Copy: Only files needed to run this application
    d) Click Publish

    http://stevenhollidge.com/blog-source-code/wcf/3.PNG

    4) You'll notice VS automatically creates a WcfJsonRestService.Service1.svc, which only consists of the following text:

    <%@ ServiceHost Service="WcfJsonRestService.Service1" %>

    5) You should now be able to navigate to your service and execute the method:
    a) http://localhost/Service1/WcfJsonRestService.Service1.svc
    b) http://localhost/Service1/WcfJsonRestService.Service1.svc/data/10

    http://stevenhollidge.com/blog-source-code/wcf/4.PNG
    http://stevenhollidge.com/blog-source-code/wcf/5.PNG

    Let me know how you get on, here's a couple of good reference links:

    Configuring WCF
    http://msdn.microsoft.com/en-us/library/aa751792.aspx

    Simple REST service without svc or config
    http://geekswithblogs.net/michelotti/archive/2010/08/21/restful-wcf-services-with-no-svc-file-and-no-config.aspx

    ReplyDelete
    Replies
    1. Almost forgot, if you haven't already set up authentication within IIS for the Service1 application, you can enable Anonymous Authentication.

      IIS > {machine} > Sites > Default Web Site > Features View > Authentication > Anonymous Authentication (Enable)

      http://stevenhollidge.com/blog-source-code/wcf/6.PNG

      Hope this helps!

      Delete
    2. Got it working on my local IIS. The thing that threw me off was the request URL. In your screenshot and the above instructions, you reference the svc file whereas in the initial code and the stuff that works on the local dev, it wasn't referenced. http://localhost/service1/WcfJSonRestService.Service1.svc/data/10 vs. http://localhost:8732/Service1/data/10

      Thank you so much for your help!

      Delete
  14. Hi steven, great articile it has really helped thankyou. How would i enable JSONP?

    ReplyDelete
  15. Steve,
    Thanks for detailed info. Unfortunately I am getting following error in fault xml tag, which no one experienced. Pl guide
    The message with To 'http://localhost:8732/Service1/data/10' cannot be processed at the receiver, due to an AddressFilter mismatch at the EndpointDispatcher. Check that the sender and receiver's EndpointAddresses agree

    ReplyDelete
    Replies
    1. Steve,
      I fixed it. For reference, this is what I have done
      Assigned name to behavior



      Added behaviorConfiguration to endPoint

      Delete
    2. Hello, I have exactly same problem and cannotfix it. Can you provide us with your config file ?
      Thanks

      Delete
  16. Very good article...

    ReplyDelete
  17. Hello, I have exactly same problem and cannotfix it. Can you provide us with your config file ?
    Thanks.

    ReplyDelete
  18. Hi Anonymous, here is the link to the config

    https://gist.github.com/2783923

    ReplyDelete
  19. Always same problem for me : cannot be processed at the receiver, due to an AddressFilter mismatch at the EndpointDispatcher. Check that the sender and receiver's EndpointAddresses agree
    Any idea ?

    ReplyDelete
  20. Hi Gazous, are you trying to run the original code from the download? If not, could you send me a link to your code and I'll take a look for you.

    ReplyDelete
  21. Hi Steven, I finally fixed the issue by adding in the behavihors.
    Now I am trying to the the JSON response ?
    Do you know a way to do this ?
    Thanks for your help !

    ReplyDelete
  22. Hi Gazous, let me know what you are trying to do as the code featured in the blog works without the need to update config files or amendments for a JSON response.

    ReplyDelete
  23. Hi Steven, I foud a way to do it (ZIP th JSON response sorry for my mistake in my message...) by zipping a JSON string in a JSON response and it works fine ! Now I am trying to have a POST request working but it is not. i always have a "Method not allowed" reponse. Any idea ?

    ReplyDelete