WCF Rest Services: Part 1 -- Getting Started
In this blog post, we'll walk through building a WCF Rest service in C# 4.0, from setup to deployment. Without further adieu, let's begin!
To get started, it's assumed you're working in .Net 4.0 and that you have some experience with C#.net programming. Open your visual studio IDE, select Tools --> Extension Manager --> Online Gallery --> Templates --> WCF --> WCF REST Service Template 40(CS) [at the time of this posting, the template is not available in the Express Edition]. Install the template (which will give it a Green Check):

After installing the template start a new project by selecting Web --> WCF REST Service Application. Create the project in a logical location on your drive and name the project PublisherService:

The default project will show an object called "Sample Item" and will also give examples of ways to interact with the object using the service. This is a great thing to know and study, but for our purposes, we will be removing this and starting fresh with our own objects and code. Delete the SampleItem.cs class and in the Service1.cs class, remove all code inside of the class, and the comments around the top of the class:

Rename the service to AcmePublisherData and then change the file name to match (AcmePublisherData.cs). Note: it is very important that after renaming the service file, the Global.asax.cs file must be modified to allow the service to startup. Since we're using a "file-less" service, the route to the service must be registered. In the future we may find a need to customize the service host factory by writing our own version, but for now we'll continue with the default version. Modify the RegisterRoutes() method to resolve to the AcmePublisherData type and also remove the "Service1" from the default path (this is the root of the URI, so it can be customized, but we're going to eliminate it for now). The code should now look like this:
At this point we'll take a short break from the project to make sure that everything is ready to go. The rest of the entry will assume that you are running and have access to a SQL2008 Server with a database called 'pubs' that contains pre-written stored procedures. Additionally, you must have already built your own connection class to deal with your specific connection to the database. This entry will not be covering the various nuances for that. In my code, I've written a simple database library that wraps the EnterpriseLibrary available from Microsoft. My service will include the project 'majorguidance.database' which will be leveraged for all database connections. Therefore, all data calls will be up to your system to implement, and the service will work once your data layer is completed. I may cover building a data layer in another post, but not at this time. If you would like to have the scripts for the database so you can create your own version, you may download them here.
Back to the project we'll create three simple get methods to get different sets of data. In REST architecture, we leverage the URL in order to get specific sets of data. There are many ways to do this, and it will be up to you on what makes the most sense for your implementation and your URL path formatting. For our projects, we'll have the ability to get all publishers, to get one publisher by an ID, to get all authors, a specific author by ID, and all titles by author. We'll cover sending data in the next part of this series, and the very next part will cover consuming this data from an ASP.Net WebPage.
To start on the very first service method, then, we'll create a method in our AcmePublisherData service called "Publishers()." Since the web type will tell us the operation, we don't need to preface this with "get," however you may do this if you wish. The method name is actually irrelevant, as we'll never use this in code (when we build the consumer webpage next time, this will become more clear). There are two important attributes to add to our method. The first will determine the web method type (POST/GET/PUT/DELETE) and the second will just be the OperationContract attribute.
Before adding the WebGet/WebInvoke tag to our method, we should determine a suitable URL path schema. For our system, we'll use the following:
You may have noticed the last schema is different. This is just to show that it is possible to send parameters directly in the uri or they may be named.
Whenever we are doing a simple GET, we'll only use the WebGet attribute, and give it the official URI to the path we'll be sending to access the service. This will be done in a format such as:
For our project, building the first method gives us the following code:
Please note that we're returning a DataSet here. DataSets only work with .Net projects, so if you need to make the project universal for consumption by Java or another language, you'll want to avoid using datasets. Use something like a generic list or build your own custom objects that can be serialized for transmission.
The next step is to write the code to access the database. This will mostly be on your shoulders, but I'll show my code that uses the EnterpriseLibrary to call a SPROC as required:
Again, my code will not work in your environment, so you must put your database handling in place and call the correct stored procedure as shown. It's finally time to see if it's all working! Pressing F5 and running the project spins up the browser:

Unfortunately, this really isn't useful. The URI contains the port that is currently being used, and if you want you can change that in the project properties, but for now there is no need to change it. Later we'll publish to IIS and the port will be irrelevant. Without a service file, though, what do we do?
REST services have a handy help file that goes along with the service automagically. Therefore, all we need to do is add "/help" to the URI and the service will suddenly display an overview, including relevant URL information and a list of available methods. This new page should look as follows:

Notice the layout, as it shows the URI, the Method type, and the escription URL. Clicking on "GET" will lead us to the page that shows the specifics of the method:

Copy and paste the service method URL into a browser tab and hit enter, and the page will then display the data as returned. If you are using IE9, you may see an error that says:
If you see this error, hit the compatibility mode icon near the URL path at the top and then hit it again and the XML will display (NOTE: chrome and firefox will only show the data, not the XML):

Add the next method to get the Publisher by ID. There are a few things of note:
Running the service and going to the help page reveals yet another quirk: "Where is my new method!" It's there, but the help page seems to be cached, so hitting f5 on the help page will list your new method. Remember this each time you add a new method or it could be frustrating!
Enter a valid PublisherID into the URL and, viola, here is the specific publisher information (try pubID 9999):

Create the remaining methods in a similar fashion (code will follow at the end). Running the help page will list all five methods, and each will be accessible as before. Try entering data in the query string for the different methods, especially to see the difference in the titles by author vs. the author by ID.

Finally, it's time to deploy. Create a new Application pool for WCFRest Services running the 4.0 environment, and create a new application for our new service in a logical location on your computer (either virtual or direct) and then publish the service to the correct folder location. At that point, you should be able to browse directly to the path without running the project. When we get to the client, we'll discuss how to debug the service after deployment, as well as how to directly run the project in the VS IDE and debug from that location (there are multiple ways to do this).
Here is a shot of the titles by author method running from IIS directly. Make sure to use a very good structure for your URL, because you'll want to make this easy to remember and deploy across multiple servers:

And here is the final code from the service:
Next time, we'll talk about how to consume the data from an asp.net WebPage. In the third session we'll discuss creating objects and sending data to the service for updating and inserting to the database.
Please send any feedback to Brian
To get started, it's assumed you're working in .Net 4.0 and that you have some experience with C#.net programming. Open your visual studio IDE, select Tools --> Extension Manager --> Online Gallery --> Templates --> WCF --> WCF REST Service Template 40(CS) [at the time of this posting, the template is not available in the Express Edition]. Install the template (which will give it a Green Check):

After installing the template start a new project by selecting Web --> WCF REST Service Application. Create the project in a logical location on your drive and name the project PublisherService:

The default project will show an object called "Sample Item" and will also give examples of ways to interact with the object using the service. This is a great thing to know and study, but for our purposes, we will be removing this and starting fresh with our own objects and code. Delete the SampleItem.cs class and in the Service1.cs class, remove all code inside of the class, and the comments around the top of the class:

Rename the service to AcmePublisherData and then change the file name to match (AcmePublisherData.cs). Note: it is very important that after renaming the service file, the Global.asax.cs file must be modified to allow the service to startup. Since we're using a "file-less" service, the route to the service must be registered. In the future we may find a need to customize the service host factory by writing our own version, but for now we'll continue with the default version. Modify the RegisterRoutes() method to resolve to the AcmePublisherData type and also remove the "Service1" from the default path (this is the root of the URI, so it can be customized, but we're going to eliminate it for now). The code should now look like this:
private void RegisterRoutes()
{
// Edit the base address of Service1 by replacing the "Service1" string below
RouteTable.Routes.Add(new ServiceRoute("", new WebServiceHostFactory(), typeof(AcmePublisherData)));
}
At this point we'll take a short break from the project to make sure that everything is ready to go. The rest of the entry will assume that you are running and have access to a SQL2008 Server with a database called 'pubs' that contains pre-written stored procedures. Additionally, you must have already built your own connection class to deal with your specific connection to the database. This entry will not be covering the various nuances for that. In my code, I've written a simple database library that wraps the EnterpriseLibrary available from Microsoft. My service will include the project 'majorguidance.database' which will be leveraged for all database connections. Therefore, all data calls will be up to your system to implement, and the service will work once your data layer is completed. I may cover building a data layer in another post, but not at this time. If you would like to have the scripts for the database so you can create your own version, you may download them here.
Back to the project we'll create three simple get methods to get different sets of data. In REST architecture, we leverage the URL in order to get specific sets of data. There are many ways to do this, and it will be up to you on what makes the most sense for your implementation and your URL path formatting. For our projects, we'll have the ability to get all publishers, to get one publisher by an ID, to get all authors, a specific author by ID, and all titles by author. We'll cover sending data in the next part of this series, and the very next part will cover consuming this data from an ASP.Net WebPage.
To start on the very first service method, then, we'll create a method in our AcmePublisherData service called "Publishers()." Since the web type will tell us the operation, we don't need to preface this with "get," however you may do this if you wish. The method name is actually irrelevant, as we'll never use this in code (when we build the consumer webpage next time, this will become more clear). There are two important attributes to add to our method. The first will determine the web method type (POST/GET/PUT/DELETE) and the second will just be the OperationContract attribute.
Before adding the WebGet/WebInvoke tag to our method, we should determine a suitable URL path schema. For our system, we'll use the following:
- PublisherData/Publishers -- to get all publishers
- PublisherData/Publishers/{id} -- to get a specific publisher
- PublisherData/Authors -- to get all authors
- PublisherData/Authors/{id} -- to get author by ID
- PublisherData/Titles?authorID={authorID} -- gets all titles by the author id
You may have noticed the last schema is different. This is just to show that it is possible to send parameters directly in the uri or they may be named.
Whenever we are doing a simple GET, we'll only use the WebGet attribute, and give it the official URI to the path we'll be sending to access the service. This will be done in a format such as:
[WebGet (UriTemplate="asdf" [, RequestFormat = WebMessageFormat.Xml]
[, ResponseFormat = WebMessageFormat.Xml] [, BodyStyle = WebMessageBodyStyle.Bare])]
//note: the items in [, ... ] above are optional parameters that can be set!
For our project, building the first method gives us the following code:
[WebGet (UriTemplate="PublisherData/Publishers")]
[OperationContract]
public DataSet Publishers()
{
//...
}
Please note that we're returning a DataSet here. DataSets only work with .Net projects, so if you need to make the project universal for consumption by Java or another language, you'll want to avoid using datasets. Use something like a generic list or build your own custom objects that can be serialized for transmission.
The next step is to write the code to access the database. This will mostly be on your shoulders, but I'll show my code that uses the EnterpriseLibrary to call a SPROC as required:
[WebGet (UriTemplate="PublisherData/Publishers")]
[OperationContract]
public DataSet Publishers()
{
try
{
//put your database logic here to access the database
string spName = "dbo.s_getAllPublishers";
//dbPubs is ultimately a decorated Enterprise Library Database object
return dbPubs.ExecuteDataset(spName);
}
catch (System.Exception ex)
{
//YOUR ERROR HANDLING SCHEMA HERE.
throw new NotImplementedException("Error handling needed");
}
}
Again, my code will not work in your environment, so you must put your database handling in place and call the correct stored procedure as shown. It's finally time to see if it's all working! Pressing F5 and running the project spins up the browser:

Unfortunately, this really isn't useful. The URI contains the port that is currently being used, and if you want you can change that in the project properties, but for now there is no need to change it. Later we'll publish to IIS and the port will be irrelevant. Without a service file, though, what do we do?
REST services have a handy help file that goes along with the service automagically. Therefore, all we need to do is add "/help" to the URI and the service will suddenly display an overview, including relevant URL information and a list of available methods. This new page should look as follows:

Notice the layout, as it shows the URI, the Method type, and the escription URL. Clicking on "GET" will lead us to the page that shows the specifics of the method:

Copy and paste the service method URL into a browser tab and hit enter, and the page will then display the data as returned. If you are using IE9, you may see an error that says:
XML document must have a top level element. Error processing resource 'http://localhost:51785/PublisherData/Publishers'.
If you see this error, hit the compatibility mode icon near the URL path at the top and then hit it again and the XML will display (NOTE: chrome and firefox will only show the data, not the XML):

Add the next method to get the Publisher by ID. There are a few things of note:
- The URI variable {id} must match the variable in the method name ..string id..
- The parameter in the method MUST be of type string
- Do not try to pass complex types this way, as they will fail (we'll cover this later).
[WebGet(UriTemplate = "PublisherData/Publishers/{id}")]
[OperationContract]
public DataSet PublisherByID(string id)
{
try
{
string spName = "dbo.s_getPublisherByID";
SqlParameter[] parms = new SqlParameter[1];
parms[0] = new SqlParameter("@pubID", Convert.ToInt32(id));
return dbPubs.ExecuteDataset(spName, parms);
}
catch (System.Exception ex)
{
//YOUR ERROR HANDLING SCHEMA HERE.
throw new NotImplementedException("Error handling needed");
}
}
Running the service and going to the help page reveals yet another quirk: "Where is my new method!" It's there, but the help page seems to be cached, so hitting f5 on the help page will list your new method. Remember this each time you add a new method or it could be frustrating!
Enter a valid PublisherID into the URL and, viola, here is the specific publisher information (try pubID 9999):

Create the remaining methods in a similar fashion (code will follow at the end). Running the help page will list all five methods, and each will be accessible as before. Try entering data in the query string for the different methods, especially to see the difference in the titles by author vs. the author by ID.

Finally, it's time to deploy. Create a new Application pool for WCFRest Services running the 4.0 environment, and create a new application for our new service in a logical location on your computer (either virtual or direct) and then publish the service to the correct folder location. At that point, you should be able to browse directly to the path without running the project. When we get to the client, we'll discuss how to debug the service after deployment, as well as how to directly run the project in the VS IDE and debug from that location (there are multiple ways to do this).
Here is a shot of the titles by author method running from IIS directly. Make sure to use a very good structure for your URL, because you'll want to make this easy to remember and deploy across multiple servers:

And here is the final code from the service:
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class AcmePublisherData
{
[WebGet (UriTemplate="PublisherData/Publishers")]
[OperationContract]
public DataSet Publishers()
{
try
{
//your database logic here...
string spName = "dbo.s_getAllPublishers";
return dbPubs.ExecuteDataset(spName);
}
catch (System.Exception ex)
{
//YOUR ERROR HANDLING SCHEMA HERE.
throw new NotImplementedException("Error handling needed");
}
}
[WebGet(UriTemplate = "PublisherData/Publishers/{id}")]
[OperationContract]
public DataSet PublisherByID(string id)
{
try
{
//your database logic here...
string spName = "dbo.s_getPublisherByID";
SqlParameter[] parms = new SqlParameter[1];
parms[0] = new SqlParameter("@pubID", Convert.ToInt32(id));
return dbPubs.ExecuteDataset(spName, parms);
}
catch (System.Exception ex)
{
//YOUR ERROR HANDLING SCHEMA HERE.
throw new NotImplementedException("Error handling needed");
}
}
[WebGet(UriTemplate = "PublisherData/Authors")]
[OperationContract]
public DataSet Authors()
{
try
{
//your database logic here...
string spName = "dbo.s_getAllAuthors";
return dbPubs.ExecuteDataset(spName);
}
catch (System.Exception ex)
{
//YOUR ERROR HANDLING SCHEMA HERE.
throw new NotImplementedException("Error handling needed");
}
}
[WebGet(UriTemplate = "PublisherData/Authors/{id}")]
[OperationContract]
public DataSet AuthorByID(string id)
{
try
{
//your database logic here...
string spName = "dbo.s_getAuthorByID";
SqlParameter[] parms = new SqlParameter[1];
parms[0] = new SqlParameter("@authorID", id);
return dbPubs.ExecuteDataset(spName, parms);
}
catch (System.Exception ex)
{
//YOUR ERROR HANDLING SCHEMA HERE.
throw new NotImplementedException("Error handling needed");
}
}
[WebGet(UriTemplate = "PublisherData/Titles?authorID={authorID}")]
[OperationContract]
public DataSet TitlesByAuthor(string authorID)
{
try
{
//your database logic here...
string spName = "dbo.s_getTitlesByAuthor";
SqlParameter[] parms = new SqlParameter[1];
parms[0] = new SqlParameter("@authorID", authorID);
return dbPubs.ExecuteDataset(spName, parms);
}
catch (System.Exception ex)
{
//YOUR ERROR HANDLING SCHEMA HERE.
throw new NotImplementedException("Error handling needed");
}
}
}
Next time, we'll talk about how to consume the data from an asp.net WebPage. In the third session we'll discuss creating objects and sending data to the service for updating and inserting to the database.
Please send any feedback to Brian
Thank you - this is very helpful. There is a lot to WCF that is difficult and confusing to sort through. This cut to the chase of what I am trying to get done.
ReplyDeleteCan u plz post the code for consuming the above wcf rest service in asp.net client application.how to retrieve that dataset returned by rest service and assign it to gridview
ReplyDeleteTry something by reading on your own
Delete