Introduction
This article shows you how easy it is to setup a web service using WCF (Windows
Communication Framework). Using the source I've provided as a template, you can quickly
create your own web services. This article does not deal with a client consuming the web
service (you will have to wait until part 2).
Background
I've been playing around with WCF for a while, since the early CTP's. I found it difficult to
find good samples/examples. Either they didn't work or I was either too lazy or too stupid to
get them working. All I wanted was something that worked with very little effort (or
understanding) on my part. I just wanted something I could install and get running!!!
Of course at some point everyone will have to understand the ABC's (but that can wait for
another day). When you start to learn a new technology, especially a Beta or CPT - you just
want it to work, figuring out how it works can wait for another day.....
So I put together a simple WCF Web service, that you can just download and get running
in a few minutes (for lazy developers - like myself!)
Getting Started
You are going to need Visual Studio 2005 (it might work with other versions of Visual
Studio, but I've not tested in and I'm not going to!!), and .NET 3.0 (I would get the entire
package from here instead).
Then just download the source from above.
Using the code
Download the example code and open up the solution in Visual Studio 2005.
The are two projects, the Web service and the Implementation of the class. [Fig 1]
Fig 1 - showing the 2 projects
I have chosen to use the dev web server that is built into Visual Studio, it's just easier, less
setup and mucking around. But there is no reason not to use IIS (if you know how to).
When your code goes into production you will be using IIS, but for now I'm going to leave it
alone.
There are two parts to the web service, the .svc file and the web.config.
WCFService.svc
Collapse | Copy Code
<% @ServiceHost Service="WCFSample.EchoImplementation"%>
Web.Config
Collapse | Copy Code
<system.serviceModel>
<services>
<service name="WCFSample.EchoImplementation"
behaviorConfiguration="returnFaults">
<endpoint contract="WCFSample.IEchoContract"
binding="basicHttpBinding"/>
<endpoint contract="IMetadataExchange"
binding="mexHttpBinding" address="mex">
</endpoint>
</service>
</services>
The Service from the ServiceHost attribute in the WCFService.svc file should match
one of the service names in the web.config. The service name and endpoint
contract should both match the implementation and contracts from the WCF Project
Template.
The WCF Project Template is made up of 3 parts, the contract [data contract or
message], the implementation and the interface [ServiceContract] (yes, the ABC's had
to come in somewhere).
The Service Contract
The WCFContract.cs contains the interface for this service.
Collapse | Copy Code
[ServiceContract]
interface IEchoContract
{
[OperationContract]
EchoMessage Echo(EchoMessage Message);
}
The Data Contract
The Message which gets sent around is contained in the WCFContract.cs
Collapse | Copy Code
[DataContract]
public class EchoMessage
{
private string _OutMessage;
private string _ReturnMessage;
[DataMember]
public string OutMessage
{
get { return _OutMessage; }
set { _OutMessage = value; }
}
[DataMember]
public string ReturnMessage
{
get { return _ReturnMessage; }
set { _ReturnMessage = value; }
}
}
The Implementation
The implementation of the web service is in WCFImplementation.cs
Collapse | Copy Code
class EchoImplementation : IEchoContract
{
public EchoMessage Echo(EchoMessage Message)
{
EchoMessage _returningMessage = new EchoMessage();
_returningMessage.ReturnMessage = Message.OutMessage;
return _returningMessage;
}
}
For this example, I used the EchoMessage to pass the data between the client and the
web service, but this could be any class that has [DataContract] as an attribute.
Introduction
Creating your first web service is incredibly easy. In fact, by using the wizards in Visual
Studio. NET you can have your first service up and running in minutes with no coding.
For this example I have created a service called MyService in the /WebServices
directory on my local machine. The files will be created in the /WebServices/MyService
directory.
A new namespace will be defined called MyService, and within this namespace will be a set
of classes that define your Web Service. By default the following classes will be created:
Global (in global.asax) Derived from HttpApplication. This file is the
ASP.NET equivalent of a standard ASP
global.asa file.
WebService1 (in WebService1.cs) Derived from
System.Web.Services.WebService. This
is your WebService class that allows you to
expose methods that can be called as
WebServices.
There are also a number of files created:
AssemblyInfo.cs Contains version and configuration information for your
assembly.
web.config Defines how your application will run (debug options, the use
of cookies etc).
MyService.disco Discovery information for your service.
WebService1.asmx Your WebService URL. Navigate to this file in a browser and
you will get back a user-friendly page showing the methods
available, the parameters required and the return values.
Forms are even provided allowing you to test the services
through the web page.
bin\MyService.dll The actual WebService component. This is created when
you build the service.
The class for your service that is created by default is called (in this case) WebService1,
and is within the MyService namespace. The code is partially shown below.
Collapse | Copy Code
namespace MyService
{
...
/// <summary>
/// Summary description for WebService1.
/// </summary>
[WebService(Namespace="http://codeproject.com/webservices/",
Description="This is a demonstration WebService.")]
public class WebService1 : System.Web.Services.WebService
{
public WebService1()
{
//CODEGEN: This call is required by the ASP+ Web Services Designer
InitializeComponent();
}
...
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
}
}
A default method HelloWorld is generated and commented out. Simply uncomment and
build the project. Hey Presto, you have a walking talking WebService.
A WebService should be associated with a namespace. Your Wizard-generatedservice will
have the name space http://tempuri.org. If you compile and run theservice as-is you'll get
a long involved message indicating you should choose a new namespace, so we add the
namespace, and the WebService description as follows:
Collapse | Copy Code
[WebService(Namespace="http://codeproject.com/webservices/",
Description="This is a demonstration WebService.")]
public class WebService1 : System.Web.Services.WebService
{
...
To test the service you can right click on WebService1.asmx in the Solution Explorer in
Visual Studio and choose "View in Browser". The test page is shown below,
When invoked this returns the following:
Getting the demo application to run
If you downloaded the source code with this article then you will need to create a directory
'WebServices' in your web site's root directory and extract the downloaded zip into there.
You should then have:
Collapse | Copy Code
\WebServices
\WebServices\bin
\WebServices\WebService1.asmx
...
Navigating to http://localhost/WebServices/WebService1.asmx won't show you the
WebService because you need to ensure that the webservice's assembly is in the
application's /bin directory. You will also find that you can't load up the solution file
MyService.sln. To kill two birds with one stone you will need to fire up the IIS management
console, open your website's entry, right click on the WebServices folder and click
Properties. Click the 'Create' button to create a new application the press OK. The
/WebServices directory is now an application and so the .NET framework will load the
WebServiceassembly from the /WebServices/bin directory, and you will be able to load
and build the MyService.sln solution.
Extending the example
So we have a WebService. Not particularly exciting, but then again we haven't exactly
taxed ourselves getting here. To make things slightly more interesting we'll define a method
that returns an array of custom structures.
Within the MyService namespace we'll define a structure called ClientData:
Collapse | Copy Code
public struct ClientData
{
public String Name;
public int ID;
}
and then define a new method GetClientData. Note the use of the WebMethod attribute
in front of the method. This specifies that the method is accessible as a WebService
method.
Collapse | Copy Code
[WebMethod]
public ClientData[] GetClientData(int Number)
{
ClientData [] Clients = null;
if (Number > 0 && Number <= 10)
{
Clients = new ClientData[Number];
for (int i = 0; i < Number; i++)
{
Clients[i].Name = "Client " + i.ToString();
Clients[i].ID = i;
}
}
return Clients;
}
If we compile, then navigate to the the .asmx page then we are presented with a form that
allows us to enter a value for the parameter. Entering a non-integer value will cause a type-
error, and entering a value not in the range 1-10 will return a null array. If, however, we
manage to get the input parameter correct, we'll be presented with the following XML file:
It's that easy.
Caching WebServices
Often a WebService will return the same results over multiple calls, so it makes sense to
cache the information to speed things up a little. Doing so in ASP.NET is as simple as adding
a CacheDuration attribute to yourWebMethod:
Collapse | Copy Code
[WebMethod(CacheDuration = 30)]
public ClientData[] GetClientData(int Number)
{
The CacheDuration attribute specifies the length of time in seconds that the method should
cache the results. Within that time all responses from theWebMethod will be the same.
You can also specify the CacheDuration using a constant member variable in your class:
Collapse | Copy Code
private const int CacheTime = 30; // seconds
[WebMethod(CacheDuration = CacheTime)]
public ClientData[] GetClientData(int Number)
{
Adding Descriptions to your WebMethods
In the default list of WebMethods created when you browse to the .asmx file it's nice to
have a description of each method posted. The Descriptionattribute accomplishes this.
Collapse | Copy Code
[WebMethod(CacheDuration = 30,
Description="Returns an array of Clients.")]
public ClientData[] GetClientData(int Number)
{
Your default .asmx page will then look like the following:
There are other WebMethod attributes to control buffering, session state and transaction
support.
Deploying the WebService
Now that we have a WebService it would be kind of nice to allow others to use it (call me
crazy, but...). Publishing your WebService on your server requires that your solution be
deployed correctly. On the Build menu of Visual Studio is a "Deploy" option that, when first
selected, starts a Wizard that allows you to add a Deployment project to your solution. This
creates an installation package that you can run on your server which will create the
necessary directories, set the correct parameters and copy over the necessary files.
This doesn't really give you an idea of what, exactly, is happening, so we'll deploy our
MyService manually.
Deploying the application is done using the steps in Getting the demo application to run.
We need to create a directory for our service (or use an existing directory) for our .asmx
file, and we need to have the service's assembly in the application's bin/ directory. Either
place the .asmx file in a subdirectory on your website and place the assembly in the /bin
folder in yourwebsite's root, or place the /bin in the subdirectory containing the .asmx file
and mark that directory as an application (see above).
If you choose to create a separate directory and mark it as an application then Within this
directory you need to add the following files and directories:
MyService.asmx This file acts as the URL for your service
MyService.disco The discovery document for your service
Configuration file for your service that overrides default web settings
web.config
(optional).
/bin This directory holds the assembly for your service
/bin/MyService.dll The actual service asembly.
Introduction
We can now use ASP.NET to create Web Service that is based on industrial standards
included XML, SOAP and WSDL.
ASP.NET Web Services support clients using HTTP-POST, HTTP-GET and SOAP protocols to
invoke methods exposed, depends on your specific requirement you choose one method
over the others. The main difference between HTTP-GET or HTTP-POST and SOAP is the
data types supported by SOAP is much richer because SOAP used XSD schema to represent
complex data types.
Here are samples codes I use to test the building of ASP.NET Web Service:
Step 1: Create the ASP.NET Web Service Source File
ASP.NET Web Service file name has extension asmx and my file is named
MyWebService.asmx, source is listed as follows:
File: MyWebService.asmx
Collapse | Copy Code
<%@ WebService Language="C#" class="MyClass" %>
using System.Web.Services ;
public class MyClass
{
[WebMethod()]
public int Add ( int a, int b)
{
return a + b ;
}
}
The page directive WebService is required and class is the name of the .NET Class to
expose the Web Service, each method exposes as Web Service Class Method need to
have a declarative attribute statement [WebMethod()] in front of it. Here the .NET Class
implementation is included in the same file with ASP.NET Web Service file but it is not
mandatory and we can choose to include an external .NET Assembly to implement the
service as the following example:
File: MyWebService2.asmx
Collapse | Copy Code
<%@ WebService Language="C#" class="MyWebService.MyStringReverse, MyWebServiceImpl"
%>
The file MyWebService2.asmx is referencing another .NET Assembly MyWebServiceImpl
which is located under the /bin ASP.NET Application sub-folder (note that the default
location for Assemblies in ASP.NET is /bin sub-folder under each ASP.NET Applications). The
source of .NET Assembly MyWebServiceImpl is written by C# and is listed as follows:
File: MyWebServiceImpl.cs
Collapse | Copy Code
namespace MyWebService
{
using System ;
using System.Web.Services ;
public class MyStringReverse: WebService
{
[WebMethod(Description="Reverse String")]
public String ReverseString ( String InString )
{
// Check null String
if ( InString == null ) return null ;
Int32 intSize = InString.Length ;
char[] arrayInString = InString.ToCharArray() ;
char[] arrayOutString = new char[intSize] ;
for (Int32 i = 0 ; i < intSize ; ++i)
arrayOutString[i] = arrayInString[intSize-i-1] ;
return new String(arrayOutString) ;
}
}
}
To create the Assembly, you can use the following command:
Collapse | Copy Code
C:\>CSC /t:library /out:bin/MyWebServiceImpl.dll MyWebServiceImpl.cs
The following sections I will continue use MyWebService.asmx as my experimental Web
Service.
Step 2: Create the ASP.NET Web Service Clients
There are many ways to consume Web Services and have three examples. The first one
uses HTTP-POST protocol and it has advantage to coexist with today’s application quite well
and use HTTP-GET is similar and I let reader to try it. The second one uses SOAP Proxy
Client Object generated by WSDL utility and it provides programmers with their familiar
object modal that they call methods provided by the generated Proxy Interface. The final
one uses SOAP standard request message and it parses SOAP response message with the
help of XMLHTTP COM object that is installed by Microsoft XML Parser 3.0.
Client use HTTP-POST Method
The example is an ASP.NET page TestWebService.aspx and source listing as follows:
File: TestWebService.aspx
Collapse | Copy Code
<html>
<body>
<form action="http://localhost/ASP.NET/MyWebService.asmx/Add" method="POST">
<input name="a"></input>
<input name="b"></input>
<input type="submit" value="Enter"> </input>
</form>
</body>
</html>
The ASP page accepts parameters from browser and calls the Add method of the Web
Service MyWebService via the HTTP-POST protocol, the result will be XML message and
need further parsing by the client application. To parse the response, client can use either
Java XML parser in applet or use IE5’s DOM Object.
The following is an example of XML response when parameters a=1, b=2 are inputted:
Collapse | Copy Code
<?xml version="1.0" encoding="utf-8" ?>
<int xmlns="http://tempuri.org/">3</int>
Client use WSDL Generated Proxy Object
If your client will be Windows applications or ASP.NET applications, you can use WSDL.EXE
utility to created standard .NET Assemble to provide Proxy Class for your clients.
Here are steps you can follow and try:
Use WSDL.EXE utility to create the Proxy Class source file in any language you have chosen
and here I use C# and command as follows:
Collapse | Copy Code
C:\>wsdl /language:C# /out:MyProxyClass.cs
http://localhost/ASP.NET/MyWebService.asmx
MyProxyClass.cs is generated and source listing as follows:
File: MyProxyClass.cs
Collapse | Copy Code
//------------------------------------------------------------------------------
// <autogenerated>
// This code was generated by a tool.
// Runtime Version: 1.0.2914.16
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </autogenerated>
//------------------------------------------------------------------------------
//
// This source code was auto-generated by wsdl, Version=1.0.2914.16.
//
using System.Diagnostics;
using System.Xml.Serialization;
using System;
using System.Web.Services.Protocols;
using System.Web.Services;
[System.Web.Services.WebServiceBindingAttribute(Name="MyClassSoap",
Namespace="http://tempuri.org/")]
public class MyClass : System.Web.Services.Protocols.SoapHttpClientProtocol {
[System.Diagnostics.DebuggerStepThroughAttribute()]
public MyClass() {
this.Url = "http://localhost/ASP.NET/MyWebService.asmx";
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/Add",
Use=System.Web.Services.Description.SoapBindingUse.Literal,
ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public int Add(int a, int b) {
object[] results = this.Invoke("Add", new object[] {
a,
b});
return ((int)(results[0]));
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
public System.IAsyncResult BeginAdd(int a, int b, System.AsyncCallback callback,
object asyncState) {
return this.BeginInvoke("Add", new object[] {
a,
b}, callback, asyncState);
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
public int EndAdd(System.IAsyncResult asyncResult) {
object[] results = this.EndInvoke(asyncResult);
return ((int)(results[0]));
}
}
Then we need to create the .NET Assembly for used by clients:
Collapse | Copy Code
C:\> csc /t:library MyProxyClass.cs
The above command will compile the source and create MyProxyClass.dll library file.
I use ASP to depict how to use the proxy object and the file is
TestWebServiceWithProxy.aspx source listing as follows:
File: TestWebServiceWithProxy.aspx
Collapse | Copy Code
<%@ page language="C#" %>
<html>
<script runat="server">
void btn_click(Object source, EventArgs e)
{
MyClass mycls = new MyClass() ;
int x = Int32.Parse(a.Text) ;
int y = Int32.Parse(b.Text);
Message.Text = mycls.Add( x, y).ToString() ;
}
</script>
<body>
<form Action = "TestWebServiceWithProxy.aspx" runat="server">
<asp:TextBox id="a" runat="server" />
<asp:TextBox id="b" runat="server" />
<asp:button id=btn OnClick="btn_click" Text="Enter" runat="server" />
<p><asp:label id="Message" runat="server" /></P>
</form>
</body>
</html>
Client use XMLHTTP to call Web service via SOAP
To fully explore the SOAP capability, you may choose to call your ASP.NET Web Service via
SOAP core protocol and here I provide another example for reference.
To test the ASP.NET service with SOAP protocol, I create an ASP client file
TestWebServiceByXML.asp and its source is listed as follows:
File: TestWebServiceByXML.asp
Collapse | Copy Code
<html>
<body>
<script language="jscript">
function btn_click (a, b)
{
var xmlObj = new ActiveXObject("Msxml2.DOMDocument") ;
var sXml = "<?xml version=\"1.0\" ?>" ;
sXml += "<soap:Envelope "
sXml += "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
" ;
sXml += "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" " ;
sXml +=
"xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" ;
sXml += "<soap:Body>" ;
sXml += "<Add xmlns=\"http://tempuri.org/\">" ;
sXml = sXml + "<a>" + a.value + "</a>" ;
sXml = sXml + "<b>" + b.value + "</b>" ;
sXml += "</Add></soap:Body></soap:Envelope>"
// Try to parse the XML string into DOM object
xmlObj.loadXML(sXml) ;
//To see the validated XML string is well-formed
XmlRequest.innerText = xmlObj.xml ;
var xmlHTTP = new ActiveXObject("Msxml2.XMLHTTP") ;
xmlHTTP.Open ( "Post", "http://localhost/ASP.NET/MyWebService.asmx",
false) ;
xmlHTTP.setRequestHeader("SOAPAction", "http://tempuri.org/Add") ;
xmlHTTP.setRequestHeader("Content-Type", "text/xml; charset=utf-8" ) ;
xmlHTTP.Send(xmlObj.xml) ;
MyResult.innerText = xmlHTTP.responseText ;
var xmlResponse = xmlHTTP.responseXML ;
answer.innerText =
xmlResponse.selectSingleNode("soap:Envelope/soap:Body/AddResponse/AddResult").text ;
}
</script>
<form>
<p>Please input a:<input id="a" name="a"></input></p>
<p>Please input b:<input id="b" name="b"></input></p>
<p>
<input type="button" id="btn" value="Enter"
onclick="jscript:btn_click(a, b)"></input>
</p>
<p>Answer is <span id="answer"></span></p>
<hr></hr>
<p>Request:</p>
<span id="XmlRequest"></span>
<p>Response:</p>
<span id="MyResult"></span>
</form>
</body>
</html>
Here I installed Microsoft XML Parser 3.0 in my client machine that give me the XMLHTTP
and DOM COM objects to test my application.
Introduction
con�sume
Collapse | Copy Code
v. con�sumed, con�sum�ing, con�sumes
v. tr.
...
2.
a. To expend; use up.
b. To purchase (goods or services) for direct use or ownership.
...
5. To absorb; engross.
Using an XML Web Service is a lot like going to a restaurant, ordering some food and
eating or "consuming" it. For this article, I have been seated at the bar of the CodeProject
Cafe and given a menu. I tell Chris (the bartender) that I want a beer and a list of the latest
articles, and in seconds he puts them in front of me. I then consume what he has given me
and go on my merry way.
But what does it take to use these services that Chris has laid bare before us? Well, that's
what this article is about, read on.
Creating a Win Forms application
For this article I will use a C# Windows application as my Web Service consumer, but you
can consume Web Services from an ASP.NET web page or any other .NET project using
VB.NET, Managed C++, or any other .NET language if you like. The demo project covered
here, will retrieve the latest articles from the CodeProject.
Creating the application is beyond the scope of this article so I will assume you already
know how to do it. If not, here are some references:
Writing a Windows Form Application For .NET Framework Using C#
Introduction to Win Forms - Part I
Adding the Web reference
To use/consume a Web Service, you must first tell your application where to find it.
In the Solution Explorer, right click on your project and click "Add Web Reference..." or
from the Project menu, click "Add Web Reference...". In the address line, type in the URL to
the Web Service. For this example, use
http://www.codeproject.com/webservices/latest.asmx.
You should see a LatestBrief web page in the browser portion of the window.
Click the Add Reference button to finish adding the Web Reference to the project.
Using the Web reference
When the Web reference is added, VS.NET uses the WSDL file to create a Reference.cs
containing the C# namespace and classes defined in the WSDL (the CP Cafe menu).
You will use the namespace, classes and methods in your application to consume the
CodeProject's "beer and latest articles".
Consumin' CP's Web services
Since the Web Reference is added, we can move onto some code. Unfortunately, you can't
really order a beer from CP (yet ;) ), but let's go ahead and see how we can get the latest
articles from the CP Web service.
Collapse | Copy Code
...
using CPConsumer.com.codeproject.www;
namespace CPConsumer
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class CPConsumerForm : System.Windows.Forms.Form
{
...
private LatestBrief cpLatestBrief = null;
public CPConsumerForm()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
// instantiate the LatestBrief class
cpLatestBrief = new LatestBrief();
}
Add using CPConsumer.com.codeproject.www;, the Web Reference namespace, for
convenience.
Declare a private member variable of the Form called cpLatestBrief of the type
LatestBrief and set it to null. This is the class generated via the WSDL that we will use.
In the constructor we instantiate it using new.
Collapse | Copy Code
private int GetNumArticles()
{
// get the maximum number of article briefs we can get from CP
if (cpLatestBrief != null)
return cpLatestBrief.GetMaxArticleListLength();
else
return 0;
}
Next, we want to find the maximum number of articles we can get from the web service.
We do this by using the GetMaxArticleListLength function defined in the WSDL and the
LatestBrief class.
We just consumed part of the CP Web service!
Collapse | Copy Code
// article brief array
ArticleBrief [] cpArticles = null;
// max article briefs
int iNumArticles = GetNumArticles();
// clear the list
ArticleList.Items.Clear();
if (iNumArticles > 0 && cpLatestBrief != null)
{
// get the article briefs from the web service
cpArticles = cpLatestBrief.GetLatestArticleBrief(iNumArticles);
if (cpArticles != null)
{
// add them all to the list view
for (int i=0; i<iNumArticles; i++)
{
ListViewItem lvi = new
ListViewItem(cpArticles[i].Updated.ToString());
lvi.SubItems.Add(cpArticles[i].Title);
lvi.SubItems.Add(cpArticles[i].Author);
lvi.SubItems.Add(cpArticles[i].Description);
lvi.SubItems.Add(cpArticles[i].URL);
ArticleList.Items.Add(lvi);
}
...
}
}
In the demo project, I fill a list view with the Latest CP articles. To get these, create an
ArticleBrief array to be filled by the GetLatestArticleBrief method.
After the array is filled, the ListView is populated with the article details. Selecting an
article in the ListView will provide you with a link and the description in labels, below the
ListView.
Conclusion
I would have never guessed that Web services would be so easy to use. Thank you Chris
M. for making the CodeProject Web services available to us.