Understanding the principles of ReST
Implementing ReST services using WCF
Consuming ReST services
Chapter 3 includes a sidebar, "Using Different Endpoints," that has a big list of binding formats you can use with WCF. I point out that for most applications, you use SOAP, or binary. That's not necessarily accurate.
Another binding is pretty popular — it is the binding that your Web browser uses to get pages from Web servers. It is called ReST, and it stands for Representational State Transfer.
In this chapter, I introduce you to ReST and guide you through its advantages and drawbacks.
ReST is basically the use of the traditional GET and POST patterns the old folks will remember from CGI. For you young pups, it is the basic format of Web requests. For instance, when you click on a link that looks like this:
http://mydomain.com/start.aspx?id=3
. . . you are using ReST. Remember, we aren't talking about an implementation here. We are talking about a remote procedure call mechanism. It is just a way to get parameters for a query to a remote machine and to get data back.
We also aren't talking about a protocol, like SOAP is. ReST is an architecture. It has guidelines, not rules.
A ReST interface has four goals. They are
Use of ReST with WCF meets most but not all of those goals. Let's take a quick ride over the details of the implementation of ReST in WCF, and you can make your own call.
There are four guiding principles to ReST. According to the standard, all ReSTful interfaces must provide interfaces that adhere to these principles. In the real world, compliance is up for discussion. Here are the principles:
Identification of resources: Individual resources (like a data item, for instance) are identified in requests (for example, using URIs in Web-based ReST systems). The resources themselves are conceptually separate from the representations that are returned to the client. For example, the server does not send its database, but rather, perhaps, some HTML, XML, or JSON that represents some database records expressed, for instance, in French and encoded in UTF-8, depending on the details of the request and the server implementation.
Manipulation of resources through these representations: When a client holds a representation of a resource, including any metadata attached, it has enough information to modify or delete the resource on the server, provided it has permission to do so.
Self-descriptive messages: Each message includes enough information to describe how to process the message (for example, which parser to invoke). An example of this is the use of Internet media types, previously known as MIME types. From the media type alone, the client must know how to process its contents. If it needs to look inside the message's contents in order to understand it, the message is not self-descriptive. For example, merely using the "application/xml" media type is not sufficient for knowing what to do with its contents, unless code-download is used.
Hypermedia as the engine of application state: If it is likely that the client will want to access related resources, these should be identified in the representation returned (for example, by providing their URIs in sufficient context, such as hypertext links). This creates an environment where the software system consuming the service has more than normal knowledge of the way the data is stored.
ReST as a concept is as old as the Web, but as a Web service implementation it is fairly new — about as new as SOAP. The largest implementation of ReST as a standard is the Web itself. CGI is based on the ReST interface.
The call to a ReST interface is clearly smaller than a SOAP call. Seriously. Look at these two examples:
ReST:
POST /Start.asmx HTTP/1.1 Host: localhost Content-Type: http; charset=utf-8 http://mydomain.com/start.aspx?id=3
SOAP:
POST /Service1.asmx HTTP/1.1 Host: localhost Content-Type: application/soap+xml; charset=utf-8 Content-Length: length <?xml version="1.0" encoding="utf-8"?> <soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema- instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"> <soap12:Body> <HelloWorld xmlns="http://tempuri.org/" /> </soap12:Body> </soap12:Envelope>
The XML of SOAP kinda gets in the way.
On the other hand. SOAP has a list of security features. There is a list of transaction features. There is a list of attachment features. SOAP has a lot of features.
ReST, not so much. ReST has only four features:
GET – Request a resource
PUT – Upload a resource
POST – Submit data
DELETE – Delete resource
Looking at the WCF service that is built in Chapter 3, it seems that changing the endpoints will solve the problem. This example starts with some of the code created in Chapter 3. Then we will alter the endpoint behavior manually to create a ReST service.
Let's start by getting the code from the WCF service in Chapter 3 and tweaking it. Just follow these steps:
Create a new WCF service application.
I called mind SharpRest
.
Make a new Registration service by right-clicking the folder and choosing Add a New Service File.
Copy the code into registration.svc
and iRegistration.cs
from the similarly named files in the Chapter 3 project.
Add the Conference class and the iConference interface from Chapter 3.
Now for the editing. The class attributes you add here are important because they affect how the compiler sets up the service responses.
To implement ReST on the service side, there have to be three (Three!!! That's a bunch!) additional attributes on that service contract:
ServiceContractAttribute: Defines the interface as a service interface.
WebInvokeAttribute: Tells the compiler that this class can be called by a Web-based ReST model.
WebGetAttributeClass: Remember GET, PUT, POST, and DELETE? This tells the runtime that the class will respond to a GET.
All right, ServiceContract we got — it came with the template. We need to add the method contract. The interface now looks like this:
namespace SharpRest { [ServiceContract] public interface IRegistration { [OperationContract] [WebInvokeAttribute] [WebGetAttribute]
List<Conference> conferencesAtLocation(int locationld); } }
What does that do for us? It tells the runtime compiler that a URI can call this directly. You can check out the binding itself by looking at the test client, as shown in Figure 4-1.
That isn't all there is to it, though. We need to create a WebServiceHost.
The WebServiceHost is a ServiceHost
class that happens to handle ReST very well, so we use it for that purpose. There are a few ways to do this, just like there are in WCF. These include the following:
Any .NET application running on a box can be a host. A Windows service, console application, even a running WPF application can host a service. As long as it has access to the network device, it should work.
IIS can host the application, and you can configure it in code. This involves using a WebServiceHost object in the service codebehind
, and decorating the class with attributes for configuration.
The Web.config
file can be set up to define an endpoint: This is the easiest way, and the way we do it in Chapter 3. If configured this way, adding other endpoints to the code is a straightforward change.
There isn't a binding in the WCF Configuration tool for the ReST binding, however, so we have to do it another way. Just follow these steps:
Open the Web.Config
for the service.
There should be binding information in there from the SOAP service we built in Chapter 3. Leave that there.
Add the bold listing for the custom POX binding under theServiceModel section.
It is the first bold code in Listing 4-1.
Add the Service information into the Service section. It is the secondbold code in Listing 4-1.
Example 4-1. The ReST Service Implementation
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.web> <compilation debug="true" targetFramework="4.0"> <assemblies> <add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, Publ icKeyToken=b77a5c561934e089" /> </assemblies> </compilation> </system.web> <system.serviceModel><bindings>
<customBinding>
<binding name="poxBinding">
<textMessageEncoding messageVersion="None" />
<httpTransport />
</binding>
</customBinding>
</bindings>
<services> <service name="SHARPService.Registration"> <clear /> <endpoint address="http://localhost" binding="basicHttpBinding" name="SharpHttp" contract="SHARPService.IRegistration" listenUriMode="Explicit"> <identity> <certificateReference storeName="My" storeLocation="LocalMachine" x509FindType="FindBySubjectDistinguishedName" /> </identity> </endpoint> <endpoint address="http://localhost" binding="wsDualHttpBinding" bindingConfiguration="" name="SharpDual" contract="SHARPService. IRegistration" /> </service><service name="SHARPService.Registration">
<host>
<baseAddresses>
<add baseAddress="http://localhost" />
</baseAddresses>
</host>
<endpoint address="registration"
binding="customBinding"
bindingConfiguration="poxBinding"
contract="SHARPService.IRegistration" />
</service>
</services> <behaviors> <serviceBehaviors> <behavior> <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --> <serviceMetadata httpGetEnabled="true" /> <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> <system.webServer> <modules runAllManagedModulesForAllRequests="true" /> </system.webServer> <connectionStrings> <add name="ConferenceDbEntities" connectionString="metadata=res://*/ Sharp.csdl|res://*/Sharp.ssdl|res://*/Sharp.msl;provider=System.Data. SqlClient;provider connection string='Data Source=.SQLEXPRESS;AttachDbFil ename="C:Program FilesMicrosoft SQL ServerMSSQL10.SQLEXPRESSMSSQL DATAConferenceDb.mdf";Integrated Security=True;Connect Timeout=30;User Instance=True;MultipleActiveResultSets=True'" providerName="System.Data. EntityClient" /> </connectionStrings> </configuration>