Web services have
become the common programming model for implementing interoperability between
systems that have little or no real connectivity. Prior to Web services,
connecting applications in meaningful ways was a difficult challenge. This
challenge was exacerbated by having to cross application domains, servers,
networks, data structures, security boundaries, and the like. However, with the
prominence of the Internet, Web services have become a common model for
accessing data, performing distributed transactions, and exposing business process
workflow.
The Internet and
its supported standards around HTTP and XML make Web services possible.
However, having to
program directly against HTTP, XML, and Simple Object Access Protocol (SOAP) is
a challenging (and time-consuming) proposition. Thankfully, ASP.NET provides a
model for building and consuming XML Web services. With it, you can define a
Web service as code (an .asmx file and related class). ASP.NET will then wrap
this code as a Web service object.
This object will
know how to expose your Web service. This includes deserializing SOAP requests,
executing your .NET Framework code, and serializing your response to be sent
back to the requesting client as a SOAP message.
Additionally,
ASP.NET provides a simple client model for consuming Web services. A proxy object
is generated for you when you reference a Web service. You can then program
against this proxy object as if you were calling in-process code. The proxy
object takes care of serialization, SOAP messaging, and the related processes.
Creating an ASP.NET Web Service
An ASP.NET XML Web
service is a class you write that inherits from the class
System.Web.Services.WebService. This class provides a wrapper for your service
code. In this way, you are free to write a Web service in pretty much the same
way you would write any other class and method. The WebService class and
ASP.NET take care of the rest.
Each of these
classes controls how your Web service works and how ASP.NET and the compiler
view your service code. You can inherit from the WebService class to get access
to standard ASP.NET features. The attribute classes allow you to mark parts of
your code as related to XML Web services. Items marked for use as Web services
are identified by ASP.NET. It then knows how to deserialize requests for your
service, call your service, and serialize the response. It also handles working
with SOAP, XML, Web Services Description Language (WSDL), and the related Web
service standards. You will see how each of these classes is used for creating
Web services in the coming sections.
ASP.NET
Web Service Projects
Web services in
ASP.NET are defined by an .asmx file. This file can be added directly to an existing
Web site. This is useful if you intend for your Web site to expose Web services
in addition to Web pages. Of course, you can also create a simple Web site and
use it for only .asmx Web service files.
Another scenario
is to create a Web service project through the Add New Project dialog box.
Here, you select the ASP.NET Web service application. This generates a separate
project for your Web service application. This project has a structure similar
to a Web site. This includes a folder for App_Data, a Web.config file, and
related elements. It is important to note that in both scenarios, your Web
service is hosted within ASP.NET and therefore has access to its features
(session state, security model, configuration, and so on).
Like a Web page,
Web services are exposed through Uniform Resource Locators (URLs). This means
your domain name followed by a page name, as in http://MyDomain/MyService.asmx. The page for an XML Web service is
defined by the .asmx file. This file is nothing more than a simple text file
that is used as a pointer to the code of your Web service.
You add an .asmx
file to your site for each Web service you wish to expose. In this case, think
of a Web service like a class that only exposes methods. Therefore, each Web
service can expose multiple methods. As an example, suppose you wish to write
an XML Web service that exposes methods related to working with the author data
in Microsoft’s sample database called Pubs. You might start by creating an
.asmx file called Authors.asmx. This file would contain an @ WebService
directive that points to the actual code for the Web service. The following is
an example:
This markup is
similar to what you would see for a Web page. However, there is no additional markup
included inside a Web service. Instead, the Web service is defined entirely in code.
The
WebServiceAttribute Class
Creating an .asmx
file and exposing public methods marked as WebMethod (see later) is sufficient for
defining Web services in ASP.NET. However, there are a number of other classes that
can be used to provide additional functionality. One such class is the
WebServiceAttribute class (recall that attribute classes can be used with or
without the Attribute suffix). This class can be used to provide information
about your Web service. This information is used by clients that wish to
reference the Web service.
You can provide
both a namespace and a description of your Web service by applying the WebService
attribute and parameters to your class. The description parameter is simply
text you write to identify the high-level intent of your Web service. The
namespace parameter sets the namespace of your Web service. This should be a
domain name under your control. Visual Studio uses the tempuri.org namespace as
filler until you define your actual namespace.
As an example,
imagine again that you are creating a Web service centered on exposing author
information. You would define your class inside the code-behind file for the
.asmx file. You could then add the WebService attribute to the class, as shown
here:
//C#
[WebService(Description
= "Services related to published authors",
Namespace
= "http://tempuri.org/")]
public
class Authors
The
WebService Class
The WebService
class represents a base class for creating XML Web services in ASP.NET. This class
is similar to the Page class for Web pages. It provides access to ASP.NET
objects like Application and Session.
It is important to
note that this class is optional: You do not need to inherit from this class to
create XML Web services. Instead, you use this class as a base class only when
you wish to access and use the features of an ASP.NET application. You might,
for example, need to leverage session state between service calls. You could do
so easily by first inheriting from this class and then accessing the session
object as if you were coding an ASP.NET Web page.
The following code
shows the authors example Web service. Here, the Authors class inherits directly
from the WebService base class.
//C#
[WebService(Description
= "Services related to published authors",
Namespace
= "http://tempuri.org/")]
public
class Authors : System.Web.Services.WebService
The
WebMethodAttribute Class
Your Web service
exposes Web methods. Each of these methods provides some sort of functionality encapsulated
by the Web service. Your class can identify these methods through the use of
the WebMethod attribute. You apply this attribute to any public method in your
Web service class you wish to expose as part of your service.
You can assign the
WebMethod attribute to a class without setting any of the parameters of the
WebMethod class. This simply identifies your method as a Web service method.
However, the
WebMethod attribute class also has a number of constructors used for various groups
of parameter values. The parameters include the following:
-
enableSessionState
This parameter is used to indicate whether the given method should be able to
work with session state. You set this value to false to disable the use of
session state for the given Web method.
-
transactionOption This
parameter is used to indicate if your Web method supports transactions. The
parameter is of the type System.EnterpriseServices.TransactionOption. Web
services are stateless HTTP calls. Therefore, you can only use an XML Web
service as the root of a transaction. This means the TransactionOptions that
indicate a root transaction are equivalent (Required, RequiresNew). All other transaction
options indicate no transaction support for the given service (Disabled,
NotSupported, Supported).
-
cacheDuration
This parameter is used to define the number of seconds for which the response
should be cached by the server. This is useful if your service has a high volume
of access and the data is relatively static. In this case, you can cache
results between calls to increase performance.
-
bufferResponse
This parameter is used to indicate whether the Web service response should be
buffered back to the client.
Consider the
author example. Imagine you have a public method called GetAuthorTitles that
returns a list of titles based on the ID of a given author. Because this data
only changes when an author publishes a new book, it is also a good candidate
for caching. The following shows the WebMethod attribute applied to this
method. Here, the cacheDuration parameter is set to 5 minutes (300 seconds).
//C#
[WebMethod(CacheDuration=300)]
public
DataTable GetAuthorTitles(string authorId)
{
... }
Web
Services and Data Types
Notice that the
previous example method, GetAuthorTitles, returns a DataTable object. This is
possible as the object supports serialization. It can serialize itself into
XML. Provided the calling client is also .NET, it can pick this XML up and
deserialize it back into a strongly typed DataTable. In this case, ASP.NET does
this for you without you having to really think about it.
You might also
wish to provide instances of your own classes as return values of your
functions or as parameters. You can do so by creating the object inside the Web
service application. In this case, the compiler will take care of making the
objects you expose serializable. These objects simply need a default
constructor that does not accept parameters. You can also use the Serializable
attribute class to tag class outside your Web service. This ensures any public
members of the class can be serialized by ASP.NET.
Consuming an ASP.NET Web Service
You can consume an
ASP.NET Web service in any application capable of making an HTTP call. This
means most .NET application types can call Web services, including console
applications, Windows-based applications, and ASP.NET. This section focuses on
calling an XML Web service from an ASP.NET application.
Referencing
a Web Service
To get started,
you need to have access to a published Web service. This Web service can be published
on a server or can be another project in your same solution. Either way, as
long as it is a valid Web service, you will be able to subscribe to it.
The first step is
setting a Web reference from your Web site to the given service. You do this by
right-clicking your project file and choosing Set Web Reference. This opens the
Add Web Reference dialog box. Here, you define the URL of your service, select
the given service (.asmx file), and set a name for the reference. This name
will be used by the generated proxy class to define the namespace for accessing
your service. Once you have set the reference, Visual Studio and ASP.NET work
to generate a Proxy class for working with the service. This allows you to
write code against the service as if the service was just another class in your
application. This proxy class does all the work of communicating to and from
the Web service, serializing and deserializing data, and more.
As an example, the
Authors.asmx file has two Web methods: GetAuthor and GetAuthorTitles. The first
returns an instance of the Author class as embedded in the service class. The second
returns a DataTable. To work with the Author class (of which the Web
application knows nothing), Visual Studio generates this type from the WSDL of
the Web service and puts it inside the proxy’s namespace.
Calling
a Web Service
You call the Web
service through the proxy. This is as simple as writing code to call a method. For
example, the following code shows how you call the GetAuthor method from the
Authors .asmx Web service discussed previously:
//C#
PubServices.Authors
pubs = new PubServices.Authors();
Note that in the
preceding example code, only the call to GetAuthor actually hits the Web service.
The object creation and property gets are simply calls to the proxy object and
are thus in-process calls.
You can also bind
to the Web service call. Remember, the Web service is exposed through a local
proxy object. Therefore, you can use object data binding to bind to the given
Web service (via the proxy). As an example, recall the GetAuthorTitles Web
service. This service is accessed via a proxy object method of the same name.
You can set this method as the SelectMethod of an object data source as shown
in the following code. You can then bind the results to a GridView control.
Calling
a Web Service from Client Script Using AJAX
You can use the
AJAX functionality built into ASP.NET to call a Web service directly from client-side
JavaScript. This is useful if you wish to kick off an operation on the server
from the user’s browser. When the results are returned, the user’s browser can
then be updated.
Of course, this
all takes place asynchronously and without a browser refresh. There are a
number of steps required to set up your Web service and an AJAX client page. First,
your client-side page and your XML Web service (.asmx file) must be in the same
domain.
From there, you
should be sure to perform all of the following steps:
- Mark your Web service class
with the ScriptServiceAttribute class. This indicates that the Web methods
inside the Web service can be called from client script.
- Register the HTTP handler
ScriptHandlerFactory with your Web site. You can do so inside the
<system.Web><httpHandlers> element in the Web.config file.
Depending on your project, this handler might already be set.
- Add a ScriptManager control to
your client page. The ScriptManager control is required for all AJAX-enabled
pages. However, inside it you need to set a ServiceReference. This reference
should point to the XML Web service (.asmx file). Doing so will tell ASP.NET to
generate a JavaScript client proxy to your Web service.
- Add a JavaScript method to your
page. You can add code inside this method to call your Web service through the
client-side proxy generated by ASP.NET. Your Web method can be referenced
through the class name of the Web service and the method name of the Web
method. Of course, you can, optionally, pass parameters to the Web service.
- Optionally, you can add another
JavaScript method to your client page to serve as the callback handler. This
method will be called by the JavaScript proxy and receive the results of the
Web service call. You can use this method to update the user.
As an example,
imagine you have a simple Web service that takes a Fahrenheit value and returns
its Celsius equivalent. For starters, you would mark the Web service with the
ScriptService attribute.
//C#
[System.Web.Script.Services.ScriptService]
[WebService(Namespace
= "http://tempuri.org/")]
public
class TempConversion : System.Web.Services.WebService
{
[WebMethod]
public float
GetCelsius(float temperature)
{
return
(5 / 9) * (temperature - 32);
}
}
Next, you need to
make sure the HTTP handler ScriptHandlerFactory is registered with your site
inside the Web.config file. The following shows an example:
You now need to
create a client Web page. Remember, this Web page must be in the same domain as
the Web service. Inside the Web page markup, you add a ScriptManager control. This
control should be set to reference the actual Web service (.asmx file). The
following shows an example of this markup:
The next step
would be to add JavaScript functionality to call the Web service, passing the user’s
input. In addition, you need to write a simple callback function that takes the
results and displays them to the user. The following code demonstrates this.
The user input is inside the TextBoxTemp and the results are shown using
LabelResults.
Notice that in
this markup, the GetCelsius JavaScript method defined earlier is called when the
user clicks the Calculate button. This triggers the Web service. The results
are returned to the FinishCallback JavaScript method and then displayed in the
Label control.
Security
and XML Web Services
You have two
primary options for securing XML Web services written as .asmx files and hosted
by ASP.NET. The first is to use one of the standard ASP.NET security methods to
authenticate and authorize users. This option is similar to securing any
ASP.NET resources such as a Web page, directory, or other file. The second
approach is to write a custom security model using SOAP headers. This option
can be useful if your calling clients cannot participate in the standard,
Windows-based security models used by ASP.NET.
ASP.NET
Security
There a number of
ways you can use the authentication and authorization methods of ASP.NET to
secure your XML Web services. Thankfully, these options are not much different from
securing other ASP.NET resources. This is a result of the Web service working
much like a Web page. They both have a URL that points to a file. You can
therefore lock down this file like you would any ASP.NET resource.
Each ASP.NET
security option comes with performance versus security trade-offs. As an example,
if you are processing sensitive information such as social security numbers,
credit cards, and the like, you will want to encrypt this data as it travels
over the network. However, this encryption will decrease performance as the
calls have to be encrypted and decrypted, and the messages themselves will be
larger. On the other hand, if you are sending basic information to and from the
Web service (such as a part numbers, category identifiers, or similar details),
you can relax the need for encryption and focus instead on authenticating and
authorizing a user. This will help increase your performance and throughput. If
your Web service is meant to be public (either inside or outside the firewall),
you can always provide anonymous access
to your Web service.
The first step in
setting up your security model is determining a method for authentication. This
means determining who the user actually is. The second is to decide if the user
has authorization rights to actually access the Web service or the features the
Web service exposes. The following list describes the basic ASP.NET security
methods and a brief description of how they can be applied to Web services for
both authentication and authorization.
- Windows Basic Authentication You use
basic authentication to restrict rights to authorized users. In this case, the
users are defined on the Web server and are given filebased access rights to
the site or the service. When a user hits your service, they are challenged to
provide credentials. Of course, these credentials can be provided by the calling
client (and not the actual user). However, basic authentication sends the user and
password information from the client to the server in clear text. This can be
helpful
if your clients
are non-Windows clients. However, as the information is encoded (and not
encrypted), it can be intercepted by network monitoring tools and compromised.
- Windows Basic Authentication over SSL
This version of basic authentication encrypts the calls over Secure Sockets
Layer (SSL). This adds additional security to this type of authentication as
the name and password are encrypted. However, the entire communication, in this
scenario, is also encrypted. Therefore, while you gain in security, you lose in
performance.
- Client certificates You can use client
certificates to identify both the caller and the Web service. Certificates, in
this case, are obtained from a trusted, third-party certificate authority. The
client’s certificate is presented with the service call and verified as trusted.
You can then use Windows to map the certificate to an actual user account. You
then use the user account to define access to the given service resource.
- Windows digest This is similar to
Windows Basic. However, digest sends the user’s password in a hashed, encrypted
format so it cannot be compromised. This option does not require SSL and will
often work through default firewalls. However, platforms outside of Windows do
not support Windows digest security.
-
Forms-based authentication
Forms-based authentication is not supported for Web service scenarios.
- Windows integrated You can use Windows
Integrated security to securely pass encrypted credentials from the client to
the server. However, this option requires that both the client and the server
are running Windows.
If you are
accessing a secured service from the user’s browser, it can pass the
credentials on to the Web server where they will be evaluated for authentication.
In the case of Windows Integrated security, you must be using Microsoft
Internet Explorer on the client. That said, user client calls to a Web service
is an unlikely scenario with Web services. It is more likely that you will be
calling a Web service from code inside your Web site (running server-side). To
pass basic authentication credentials from your Web server to a Web service,
you first create a NetworkCredentials class. This class contains the user name,
password, and domain information. You can then create a CredentialCache object
to which you add the NetworkCredentials instance. You then set the Web
service’s generated client proxy’s Credentials property to the newly created
CredentialCache object. If you are using integrated security between the Web
server and the Web service, you set the Credentials property of the Web service
proxy class to System.Net.CredentialCache .DefaultCredentials. The Web server
running your Web page will then pass credentials to the Web service.
Custom
Security with SOAP Headers
You can also use
SOAP headers to write a custom mechanism for passing user information into a
Web service. Because this option uses Web service standards and not Windows,
you can use it to work in scenarios where you require access to your service
from other platforms besides Windows.
Custom SOAP
headers can be used in a secure, encrypted manner. However, the encryption is
optional and up to you to write (using the .NET Framework, of course). You can
also use SOAP headers to send information to the service as plaintext
(unencrypted). This is useful if you need to pass along information or you are
behind a trusted firewall. It is not, however, a best practice to send
unencrypted user information (name and password) using a SOAP header.
There are no
default, built-in features for working with custom SOAP headers in
authentication scenarios. Instead, both the client and the service need to be
aware of how to format and pass the header information. In addition, on the
server, you need to implement the IHttpModule interface to intercept the SOAP
request, get the SOAP header, and parse (and decrypt) the user information. If
the operation fails, you throw a SoapException instance.
Shembull 1:
1. Open Visual
Studio and create a new ASP.NET Web Service Application project using C#. Name
the project pubsservices.
2. Delete
Service.asmx (and its code-behind fi le) from your project. Add a new service
file called authors.asmx by right-clicking the project and choosing Add New
Item. Select the Web Service template from the Add New Item dialog box.
3. Open the
code-behind fi le for Authors.asmx in the code editor. Delete the default code
in the service fi le template. Add a new class definition for the Authors
service. There is no need to inherit from the WebService class as this service
does not use the features of ASP.NET. Tag the class with the
WebServiceAttribute class and pass a default namespace. Your class definition
should look similar to the following:
//C#
namespace
PubsServices
{
[WebService(Namespace
= "http://tempuri.org/")]
public class
Authors
{
}
}
4. Open the
Web.config file. Find the <connectionStrings
/> element.
User
Instance=True" providerName="System.Data.SqlClient"/>
</connectionStrings>
5. Return to the
.asmx service file. Add using statements to the class file for System.Data,
System.Data.SqlClient, and System.Configuration.
6. Add a private
variable at the class level to store the connection string to the Pubs
database. Name this variable _cnnString, as shown in the following code:
7. Add a method to
the class to return all titles for a given author based on their authorId. These
authors can be returned as a DataTable instance. Name this method GetAuthorTitles.
8. Tag the
GetAuthorTitles method with the WebMethodAttribute class. Set the CacheDuration
to 300 seconds. Your method should look as follows:
1.Continue
editing the project you created in the previous exercise.
2.Add
a new Web site to the solution: Right-click the solution and choose Add | New
Web Site. Select the ASP.NET Web Site template. Name the Web site pubsclient. Right-click
the Web site and choose Set As StartUp Project.
3.Add
a Web reference to the Web service created in Exercise 1. Start by rightclicking the Web site; choose Add Web Reference. In
the Add Web Reference dialog box, select Web Services In This Solution. This
should display the Authors service; click it. On the right side of the dialog
box, change the Web reference name to pubsservice. Finish by clicking Add
Reference.
4.Open
the Default.aspx page in your Web site. Add an object data source control to
the page. Configure it to use the Web service proxy class. Set the authorId
parameter to be set via the query string value auId.
Add
a GridView control to the page and set its DataSourceId property to the object
data source. Your markup should look as follows:
You learned about
creating XML Web services with ASP.NET. This is a very useful, straightforward
way to create Web services that you intend to host in IIS and call over HTTP.
However, the service model can be extended beyond HTTP. For example, you might
want to write a service that is accessed inside the firewall over Transmission
Control Protocol (TCP) instead of HTTP. This can provide increased performance
in this scenario. In earlier versions of the .NET Framework, this meant you
wrote the service using Remoting. However, if that same service code needed to
be called over both HTTP and TCP, you had to write and host it twice. This is
one of the many problems WCF is meant to solve.
WCF is a unifying
programming model. It is meant to define a singular way for writing services
and thereby unify things like Web services (.asmx), .NET Remoting, Message
Queue (MSMQ), Enterprise Services (COM+), and Web Services Enhancements (WSE).
It does not replace these technologies on an individual basis. Instead, it
provides a single programming model that you can use to take advantage of all
of these items at once. With WCF, you can create a single service that can be
exposed as HTTP, TCP, named pipes, and so on. You also have multiple hosting
options.
This lesson covers
the basics of WCF to give you a solid footing when working with this technology.
This lesson is not all-encompassing on WCF. Rather, it focuses on those areas
inside WCF that are specific to an ASP.NET developer: writing, hosting, and
calling WCF services with ASP.NET Web sites.
Presenting
Windows Communication Foundation (WCF)
Before you build
your first WCF service application, it is important to get an overview of how the
technology works. WCF enables message-based communication to and from
endpoints. You write your service and then attach, or configure, endpoints. A
given service can have one or more endpoints attached to it. Each WCF endpoint
defines a location to which messages are sent and received. This location
includes an address, a binding, and a contract. These address, binding, and
contract concept is often referred to as the ABCs of WCF. The following list describes
each of these items in detail:
A
is for address
The endpoint’s address is the location of the service as a Uniform Resource
Identifier (URI). Each endpoint of a given service is meant to have a unique address.
Therefore, if you have a service that exposes more than one endpoint (or transport
protocol), you will uniquely identify the address based on the endpoint’s transport
protocol. This might mean changing a port number, defining the address as HTTPS,
or taking a similar action.
B
is for binding
The binding defines how the service is meant to communicate, such as HTTP, TCP,
MSMQ, Binary HTTP, and so on. This is referred to as the binding’s transport.
You can add multiple bindings to a single service. Bindings can include other information,
too, like encoding and security. Each binding must, at a minimum, define a
transport.
C
is for contract
The contract represents the public definition, or interface, of the service. It
defines things like the namespace of the service, how messages should be sent,
callbacks, and related contract items. There are multiple contract types in
WCF, including service contract, operation contract, message contract, fault
contract (for error handling), and data contract. These contracts work together
to indicate to the client code consuming the WCF service how it should define
communication messages.
Once you define
your WCF service and configure at least one endpoint, you must host it. There
are a few options here, which are discussed in a moment. However, the one host this
lesson focuses on is IIS and ASP.NET. To call the service, a client generates a
compatible endpoint. This endpoint indicates where the service is, how communication
should work, and the format of that communication. At run time, clients
typically initiate requests to a listening, hosted service. Like a Web service,
the WCF service processes the request and returns results—all using the defined
endpoint information.
The good news is
that there are multiple tools and configuration support for creating WCF services.
Again, it is still important to understand how this works. As an additional
overview, the following section presents the layers of the WCF architecture.
The
Layers of the WCF Architecture
A WCF application
has multiple layers that work together to provide a wide range of functionality
and options for building a service-oriented application (SOA). For the most
part, these layers are behind the scenes and the configuration of services is
done for you or through configuration tools that make it easier.
It is important to
understand these layers and the many options they provide you as a service
developer. The following list provides an overview of each layer.
- Contract layer The contract layer is
meant to define the contract your service exposes to end clients. This includes
the message the service supports for calling operations, receiving results, and
managing errors. In addition, the contract includes the endpoint information of
policy and binding. For example, the contract might indicate that the service
requires HTTP with a binary encoding.
- Runtime layer The service runtime layer
controls how your service is executed and how the message body is processed.
You can configure this layer to support transactions, handle concurrency, and
emit error information. For example, you can use throttling to indicate the
number of messages your service can process; you can use the instance
functionality to indicate how many instances of your service should be created
to manage requests.
- Messaging layer The messaging layer
represents the WCF channel stack in terms of transport and protocol. Transport
channels work to convert messages to work across HTTP, named pipes, TCP, and
related protocols. Protocol channels work to process messages for things like
reliability and security.
- Hosting layer The hosting layer defines
the host, or executable, that runs the service in process. Services can be
self-hosted (run in an executable), hosted by IIS, Windows Activation Service
(WAS), a Windows Service, or COM+. Picking a host for your service depends on a
number of factors like client access, scalability, reliability, and the need for
other services of the host (like ASP.NET). In most enterprise application
cases, you will want to use an existing host for your service rather than
writing your own.
You can see there
are many options for creating, configuring, and hosting a wide array of services.
Creating
a WCF Service with ASP.NET
Creating and
consuming WCF services follow a standard set of programming tasks. You follow these
steps every time you wish to create and consume a new WCF service:
1. Define the
service contract.
2. Implement (or
write) the service contract.
3. Configure a
service endpoint(s).
4. Host the
service in an application.
5. Reference and
call the service from a client application.
As you can see, a
WCF service application starts with the contract. This contract indicates the
features and functionality your service will offer to calling clients. In WCF
programming, you create this contract by first defining an interface and
decorating that interface with a number of attributes.
These WCF
attribute classes are found in the System.ServiceModel namespace. These classes
are used to define the details of the contract that your service will have with
calling clients. For example, you can indicate if your service contract is
one-way, request-reply, or duplex. These attributes also define your service operations
and the data that define these operations. The following list provides a
description for each of these classes.
- ServiceContract The ServiceContract
attribute class is used to indicate that a given interface (or class) is a WCF
service. The ServiceContract attribute class has parameters for setting things
like whether the service requires a session (SessionMode), the namespace, the
name of the contract, the return contract on a two-way contract
(CallbackContract), and more.
- OperationContract The OperationContract
attribute class is used to mark methods inside an interface (or class) as
service operations. Methods marked with OperationContract represent those
exposed by the service to clients. You can use the parameters of the
OperationContract attribute class to set things like whether the contract does not
return a reply (IsOneWay), the message-level security (ProtectionLevel), or
whether the method supports asynchronous calls (AsyncPattern).
- DataContract The DataContract attribute
class is used to mark types you write (classes, enumerations, structures) as
participating in WCF serialization via the DataContractSerializer. Marking your
classes with this attribute ensures they can be sent to and from disparate
clients in an efficient manner.
- DataMember The DataMember attribute
class is used to mark individual fields and properties that you want to
serialize. You use this class in conjunction with the DataContract class.
The
WCF Service Application
Visual Studio and
ASP.NET define the WCF Service Application project template. This template defines
a Web project that serves to host the WCF service. This project contains a
reference to System.ServiceModel.dll, which contains the WCF classes. Creating
a new instance of this project template will also generate a default service
(Service1.svc) and a related contract file (IService1.cs).
The contract file
is a regular .NET Framework interface that includes the service attribute classes
tagging the service (class), the operations (methods), and the data members
(types, fields, properties). The .svc file is a class that implements this
interface. Like other ASP.NET templates, you can use these classes to create
your own service.
Finally, a WCF
Service application is automatically configured to be hosted in IIS and expose
a standard HTTP endpoint. This information can be found inside the
<system.servicemodel> section of the Web.config file. The following code
shows an example:
As you can see,
the WCF Service application in ASP.NET takes care of many of the common steps
to a WCF service. In fact, steps 1, 3, and 4, as discussed previously, are
taken care of by default. That leaves step 2, implement the service, and step
5, call the service from a client application.
Implementing
the WCF Service
To implement the
service, you start by defining the contract via the interface. For example, suppose
you wish to create a service that exposes methods that work with the Shippers
table in the Northwind database. You might start by creating a Shipper class
and marking it as a DataContract and marking its members as DataMembers. This allows
you to pass the Shipper class in and out of the service. The following code
shows an example:
[DataContract]
public
class Shipper
{
[DataMember]
public int
ShipperId { get; set; }
//implement
remaining properties
}
The next step is
to define the methods of your interface. You need to mark those with the OperationContract
attribute. You need to mark the interface with the ServiceContract attribute.
For example,
suppose the shipping service exposes operations for retrieving a single shipper
and saving a single shipper. In this case, your interface should look as
follows:
public interface
IShipperService
{
[OperationContract]
Shipper
GetShipper(int shipperId);
[OperationContract]
Shipper
SaveShipper(Shipper shipper);
}
This interface
will be used by WCF to expose a service. Of course, the service will be configured
based on the information inside the Web.config file. The service interface also
still needs to be implemented. To do so, you implement the service interface
inside an .svc file.
For example, if
you were to implement the interface contract defined previously for working with
the Shipper data, you would do so as the following code demonstrates:
//code
to get the shipper from the db and return it
}
public void
SaveShipper(Shipper shipper)
{
//code
to save the shipper to the db
}
}
Consuming
a WCF Service in an ASP.NET Page
You are now ready
to call the WCF service created previously. The contract is defined via the IShipperService
interface. The contract is implemented inside the ShipperService.svc file. An endpoint
is configured via the default HTTP endpoint set up inside the Web.config file.
The service is hosted by IIS and ASP.NET (or your local Web server). The final
step is to set a client to call the service. In this case, we assume the client
is another ASP.NET Web site. However, it could easily be a Windows application
or another application on a different platform. To start, you need to generate
a proxy class for calling the WCF service. This can be done using Visual
Studio. You right-click your Web site and select Add Service Reference. This opens
the Add Service Reference dialog box.
This dialog box
allows you to define an address to your service. Again, this is based on the endpoint
that your service exposes. In this example, a connection is being made to the
ShipperService.svc created in the prior section. Notice how the contract is
shown via the service’s interface.
The namespace
defines the name for the proxy class that is generated by Visual Studio. This
proxy class is a WCF service client that allows you to program against the
service without having to deal with the intricacies of WCF.
You can view the
contents of the service reference by selecting Show All Files from Solution Explorer.
Of course, this only works with a Web application.
The file
Reference.cs contains the actual proxy class. The other files are used by this
proxy class when working with the service. This proxy class communicates with
the Web service. In fact, it contains classes and methods that look just like
those of the service, thanks to the service contract. Notice you can call the
ShipperServiceClient code and even pass a local type called Shipper that
contains the same properties defined by the service contract.
Your client code
must also define binding and endpoint information. The Add Service Reference
task generates the appropriate endpoint information automatically when you add the
service reference. This information can be found inside the Web.config file of
the service client Web site. The following shows an example:
You can edit the
WCF configuration information directly in Web.config. Alternatively, you can
use the Service Configuration Editor to manage your endpoints (for both clients
and the services you create). To do so, right-click the Web.config file and
choose Edit Wcf Configuration.
Calling a WCF
Service from Client Script Using AJAX (REST and JSoN) WCF allows you to create
and work with a number of different types of services. Remember, it is a
technology used to define service endpoints that have an address, binding, and
contract.
This level of flexibility built into the framework allows it to support various message types
and communication protocols.
One such service
type is based on representational state transfer (REST) and JavaScript Object
Notation (JSON). Services based on these concepts have become very popular as
the result of AJAX programming. AJAX becomes easier with a simple service
(REST) based on a simple message format (JSON). WCF and the .NET Framework have
built-in support for both.
A REST service is
a Web service you write that responds to HTTP GET requests. Clients can therefore
call a REST service the same way they would access a page: using a URL and a
query string. The server then responds with a text document as it would for any
HTTP GET request.
This way, a REST
service does not require knowledge of the XML schema used to call the service. Instead,
it simply sends the request and processes the text-based response (usually JSON
formatted data).
The response of a
REST service is typically in the form of JSON data. JSON is a message data
format that evolved out of the heavy use of AJAX. The message format is not
XML-based (as are most services). Instead, it is simple, lightweight, and
text-based. A JSON message can be processed easily by the JavaScript engine
that exists inside nearly all Web browsers. This makes it ideal when calling
services from JavaScript. In fact, a JSON message can be parsed using the
JavaScript eval function because basically it is syntactically formatted
JavaScript. The following is an example of a JSON-formatted message:
{
"proudctName":
"Computer Monitor",
"price":
"229.00",
"specifications":
{
"size":
22,
"type":
"LCD",
"colors":
["black", "red", "white"]
}
}
Writing
a WCF Service Based on REST and JSON
Creating a WCF
service based on REST and JSON is somewhat simplified in ASP.NET. This is due
in part to the AJAX support built into ASP.NET. Because of this, there is a WCF
template that you can use to quickly create a service that leverages the REST
calling mechanism and the JSON data format.
This AJAX-WCF item
template is the AJAX-enabled WCF Service template. It can be found in the Add
New Item dialog box for a Web site project. The template defines a class that
can be used to create a WCF service for use with AJAX.
As an example,
suppose you were creating a service to calculate a product’s full price based
on the item ID and the postal code to which you are shipping the item. The
following code shows an example of an AJAX-enabled WCF Service that simulates
such a method:
public
double CalculatePrice(string itemId, string shipToPostalCode)
{
double
price;
//simulate
product price lookup based on item id
price
= 45;
//simulate
calculation of sales tax based on shipping postal code
price
= price * 1.06;
//simulate
calculation of shipping based on shipping postal code
price
= price * 1.1;
return
price;
}
}
}
Notice that the
service method is marked with the WebInvoke attribute. This indicates that the
method can be called by an HTTP request. Methods marked with WebInvoke are
called using HTTP POST. This can be important if you are sending data to the
server to be written or do not wish your request to be cached by a browser or
the server. If, however, your service typically returns data that is somewhat
static, you might mark the method with the WebGet attribute. This indicates an
HTTP GET request with results that can be cached. This is the only reason to
use the WebGet attribute. The ASP.NET AJAX ScriptManager control can work with both
HTTP GET and POST services.
Visual Studio also
updates the site’s Web.config file when the AJAX-enabled WCF Service is added
to the project. The following shows an example. Notice the element
<enableWebScript />. This indicates that the endpoint is a RESTful
service that uses the JSON data format and can therefore be consumed by AJAX.
Also notice that the binding is set to webHttpBinding indicating again that
this service is called via HTTP (and not SOAP).
As you can see,
ASP.NET simplifies creating WCF services based on REST and JSON. You can also
use the features of WCF and the .NET Framework to define REST services and
JSONbased messages outside of ASP.NET. In fact, the .NET Framework supports
serializing between .NET types and JSON data structures.
Calling
a JSON-Based WCF Service from AJAX
The AJAX support
in ASP.NET also makes calling a REST-based service from AJAX a relatively straightforward
process. The ScriptManager control allows you to set a service reference to the
given RESTful WCF service. It then defines a JavaScript proxy class for you to
call. This proxy class manages the call from the AJAX-enabled page to the WCF
service.
For example, to
call the service defined previously, you start by adding a ScriptManager control
to your page. You then define a ServiceReference to the actual service. The
following markup shows an example:
You can then
define a script block on your page to call the proxy class generated for you based
on this service reference. In the example, the service takes a product ID and a
postal code. The following JavaScript assumes these values are entered by a
user in a couple of text box controls. The code also responds to a user
clicking a button on the page.
Notice that in the
previous code, the call to the CalculatePrice method goes through a proxy that
defines some additional parameters. This allows you to pass in a JavaScript
method name to be called by the ScriptManager after the service is called. You
can define a method both for success and for failure. In this case, a
successful call writes the results to a Label control.
The following code
shows the markup for the page’s controls to round out the example:
1. Open Visual
Studio and create a new WCF Service Application project using C#. Name the
project northwindservices.
2. Delete the
IService1.cs and Service1.svc files from the project.
3. Add a new WCF
service to the application: Right-click the project and choose Add |New Item.
Select the WCF Service template. Name the service shipperservice.svc. Notice
that both an interface file (IShipperService) and an .svc file are created.
4. Open Web.config.
Navigate to <system.serviceModel>. Delete both the <service> and <behavior>
nodes for Service1.
Navigate to the
<connectionStrings> node in Web.config. Add a connection string for the
Northwind database. This connection string should read as follows (formatted to
fit on the printed page):
User
Instance=True" providerName="System.Data.SqlClient"/>
</connectionStrings>
5. Open IShipperService.cs.
Define a data contract class that represents a Shipper object. Remember to use
the DataContract and DataMember attributes. Your code should read as follows:
namespace
NorthwindServices
{
[DataContract]
public class
Shipper
{
[DataMember]
public
int ShipperId { get; set; }
[DataMember]
public
string CompanyName { get; set; }
[DataMember]
public
string Phone { get; set; }
}
}
6. Next, define
the interface for the Shipper class. Create one method for returning a Shipper
instance and another for accepting a Shipper instance for updating. Remember to
use the ServiceContract and OperationContract attributes. Your code should look
as follows:
[ServiceContract]
public
interface IShipperService
{
[OperationContract]
Shipper
GetShipper(int shipperId);
[OperationContract]
void SaveShipper(Shipper shipper);
}
7. Open the
ShipperService.svc code by double-clicking the fi le. Add using statements for System.Data, System.Data.SqlClient,
and System.Configuration. Add code to get the connection string from the
database. Implement the GetShipper method by calling the database to retrieve a
record from the Shipper table. Copy this data into a Shipper instance and
return it as a result of the function.
Implement the
SaveShipper method by updating a shipping record with data inside a Shipper
instance. Your code for the service implementation should read as follows:
8. Compile and run
your Service application. Doing so should launch the service in a Web browser.
9. Click the link
at the top of the Web page to see the WSDL for your WCF service.
10. Generate a
test client for your WCF service using Svcutil.exe. Start by opening a Visual Studio
command prompt (Start | All Programs | Microsoft Visual Studio 2008 | Visual Studio
Tools). Navigate to a directory to which you wish to generate the test client. Copy
the command from the top of the Web page into the command window and press
Enter. Exit the command prompt.
11. Navigate to
the files you generated. Open these files and examine them. There should be a
configuration file that defines an endpoint for communicating with the service.
There should also be a code file that shows how to call the WCF service. This
is the proxy class you can use to write an actual client. These are also the
same files that get generated when you set a service reference to a WCF
service.
Shembull
2:
1. Continue editing the project you created in the
previous exercise.
2. Add a new Web site to the solution: Right-click
the solution and choose Add | New Web Site. Select ASP.NET Web Site. Name the
Web site ShipperClient. Right-click the Web site and choose Set As StartUp
Project.
3. Right-click the newly added Web site and choose
Add Service Reference. Click Discover in the Add Service Reference dialog box.
Select the ShipperService. Set the namespace to NwServices. Click OK to close
the dialog box.
4. Open the Default.aspx page. Add controls to the
page to allow a user to select a shipper based on their ID, display the details
of that shipper, edit them, and save them back to the WCF service.
5. Add code to the Select button’s click event to
call the WCF service and put the results into the Shipper Data form. Your code
should look as follows:
6. Add code to the
Save button’s click event to call the WCF service with the values from the
various TextBox controls. Your code should look as follows: