Service Development

Description Article
Table of Contents
Description

The usual implementation process of WCF services is also valid for ReST services.

Required namespaces:

System.ServiceModel.Web
System.Uri.Template

Pure ReST URL

WCF ReST services, when hosted on IIS still have the “.svc” extension in the URL.

In order to surpass this situation refer to the articles bellow:

[1] http://stackoverflow.com/questions/355165/how-to-remove-thie-svc-extension-in-restful-wcf-service

[2] http://www.west-wind.com/weblog/posts/2008/Dec/15/Removing-the-SVC-Extension-from-WCF-REST-URLs

Operations
Service Definition: Implementing URI Design

The URI templates of the services are defined in conjunction with the methods exposed.

WCF provides a additional classes, other then System.Uri for specifically dealing with URI templates, variables, and matching parameters.

How to do it

The UriTemplate object expects a URI template string.

These templates may contain variables within curly braces (“{username}?tag={tag}”) and even an asterisk (“*”), which acts as a wildcard, when  to match anything from that point on in the path.

Default variable values can also be defined within the template, making it possible to omit that part of the path:

  • a template of “{username}/{tag=all}” means that the variable “tag” will have the default value of “all” when that path segment is omitted.

Uri template Sample

UriTemplate uriTemplate = new UriTemplate("users/{username}/bookmarks/{id}");

The UriTemplate objects are mapped to the exposed method signatures through the WebGet and WebInvoke attributes.

Service Definition: Defining the HTTP Interface

The HTTP interface that each service will support is done through the WebGet and WebInvoke attributes and the UriTemplate and Method attribute parameters

How to do it

The main thing specified is the Uri template that the method is designed to handle and with witch HTTP verb.

The various variables indicated in the Uri template  are mapped to the exposed method parameters by simply using the same name in both places.

Note: The metod parameters must be defined with data type string.

The WebGet attribute allows the following properties to be specified:

Property Name Type Possible Values Description
BodyStyle WebMessageBodyStyle Bare
Wrapped
WrappedRequest
WrappedResponse
Controls aspects of the formatting of the serialized data
RequestFormat WebMessageFormat json
xml
Specifies the deserialization format for incoming HTTP requests
ResponseFormat WebMessageFormat json
xml
Specifies the output serialization format to use for an individual OperationContract
UriTemplate String Any string representing a parameterized relative URI The template that will be matched against incoming HTTP request URIs.

The WebInvoke attribute provides the same properties as WebGet, but with one additional property:

Property Name Type Possible Values Description
Method String “POST”, “DELETE”, “*”, etc The template that will be matched against incoming HTTP request URIs

How to use it

An OperationContract that retrieves a list of blog posts might be represented as follows:

[OperationContract]
[WebGet(UriTemplate = "/posts")]
PostResultSet GetPosts();

An OperationContract that updates an existing blog post might be represented as follows:

[OperationContract]
[WebInvoke(Method = "PUT", UriTemplate = "/admin/post/{id}")]
void UpdatePost(string id, Post post);

 

Service Definition: The Current Context

The WebOperationContext.Current represents the current context:
It exposes four “contexts” as properties, each of which relate to the HTTP request or response within the WCF request/response model:

  • IncomingRequest;
  • IncomingResponse;
  • OutgoingRequest,;
  • and

  • OutgoingResponse

The two that will typically be used the most are IncomingRequest and OutgoingResponse

How to do it

The Current Context

The WebOperationContext.Current represents the current context:
It exposes four “contexts” as properties, each of which relate to the HTTP request or response within the WCF request/response model:

    • IncomingRequest;
    • IncomingResponse;
    • OutgoingRequest;

and

  • OutgoingResponse

The two that will typically be used the most are IncomingRequest and OutgoingResponse.


Service Definition: Binding and Behavior

The System.ServiceModel.WebHttpBinding class implements the binding responsible for non-SOAP formatting of WCF messages.

The System.ServiceModel.Description.WebHttpBehavior class implements the behavior to use.

How to do it

A ReST WCF service is provided by specific configuration of the Binding element for all the WCF endpoints:

Name Purpose Scenario Remarks
WebHttpBinding Designed to be used to configure endpoints for Windows Communication Foundation (WCF) Web services that are exposed through HTTP requests instead of SOAP messages. It support HTTP and secure HTTP Interoperability scenario The endpoint behavior webHttp must be used to enable the Web programming model for a WCF service
Service Hosting
How to do it

WCF uses the webHttpBinding binding and independently of the host requires the WebHttpBehavior to be added to the endpoint.

If the “helpEnabled” option enabled on the WebHttpBehavior behavior then help pages are automatically generated for the ReST services. This option should only be active within a debug/test environment:

WCF help pages for ReST service
The automatic help page provides a list of the valid URIs, the HTTP methods that can be used with a given URI and a default description of the operation.

Note: The Help page can calso be configured on the <standardEndpoints/webHttpEndpoint> section of the configuration file.

Service Hosting in a managed application (Self-hosting)
How to do it

How is it configured

The  service is configured in the App.Config file, section “system.serviceModel“:

<system.serviceModel>
    <services>
      <service behaviorConfiguration="defaultServiceBehavior" name="WCF.ReSTServiceImpl">
        <endpoint address="rest" behaviorConfiguration="webEndPointBehavior" binding="webHttpBinding" contract="WCF.IReSTService" />
        <endpoint address="mex" bindingConfiguration="" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:35799/training/wcf/app/testservice1/"/>
          </baseAddresses>
        </host>
      </service>
    </services>

    <behaviors>
      <serviceBehaviors>
        <behavior name="defaultServiceBehavior">
          <!-- 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>
      <endpointBehaviors>
        <behavior name="webEndPointBehavior">
          <webHttp helpEnabled="true"/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
<system.webServer>

or programatically as exemplified next:

ServiceHost host = null;
try
{
	host = new ServiceHost(
                            typeof(wcfTestServiceImpl),
                            new Uri("http://localhost:35799/training/wcf/app/testservice1"));

	WebHttpBinding binding = new WebHttpBinding (WebHttpSecurityMode.None);

	ServiceEndpoint endPoint = new ServiceEndpoint(
					ContractDescription.GetContract(typeof(wcfTestServiceImpl)),
					binding,
					"/rest");

	WebHttpBehavior webBehavior = new WebHttpBehavior();
        webBehavior.HelpEnabled = true;
	endPoint.Behaviors.Add(webBehavior);

	host.AddServiceEndpoint(endPoint);

	// Configure the MEX endpoint
	host.AddServiceEndpoint(
			ServiceMetadataBehavior.MexContractName,
			MetadataExchangeBindings.CreateMexHttpBinding(),
			"mex");

	// Configure service behavior: enable service metadata 

	ServiceMetadataBehavior serviceMetadataBehavior =
					new ServiceMetadataBehavior
					{
						HttpGetEnabled = true,
						MetadataExporter = {
							PolicyVersion = PolicyVersion.Policy15
						}
					};

	host.Description.Behaviors.Add(serviceMetadataBehavior);

	// Initiate host...

	host.Open();
	Console.WriteLine("Test console host initiated: Press <enter> to close host application");
	Console.ReadLine();
}
catch (Exception ex)
{
	Console.WriteLine("Test console host could not be initiated or an error occurred during host life cycle: " + ex.ToString());
}
finally
{
	if (host != null)
	{
		if (host.State != CommunicationState.Closed) host.Close();
	}
}

If programatically then the WebServiceHost class should be used since it automatically adds the webHttpBehavior behavior to the endpoint:

WebServiceHost host = null;
try
{
	host = new WebServiceHost(
                            typeof(wcfTestServiceImpl),
                            new Uri("http://localhost:35799/training/wcf/app/testservice1"));

	WebHttpBinding binding = new WebHttpBinding (WebHttpSecurityMode.None);

	ServiceEndpoint endPoint = new ServiceEndpoint(
					ContractDescription.GetContract(typeof(wcfTestServiceImpl)),
			                binding,
					"/rest");

	host.AddServiceEndpoint(endPoint);

	// Configure the MEX endpoint
	host.AddServiceEndpoint(
			ServiceMetadataBehavior.MexContractName,
			MetadataExchangeBindings.CreateMexHttpBinding(),
			"mex");

	// Configure service behavior: enable service metadata 

	ServiceMetadataBehavior serviceMetadataBehavior =
					new ServiceMetadataBehavior
					{
						HttpGetEnabled = true,
						MetadataExporter = {
							PolicyVersion = PolicyVersion.Policy15
						}
					};

	host.Description.Behaviors.Add(serviceMetadataBehavior);

	// Initiate host...

	host.Open();
	Console.WriteLine("Test console host initiated: Press <enter> to close host application");
	Console.ReadLine();
}
catch (Exception ex)
{
	Console.WriteLine("Test console host could not be initiated or an error occurred during host life cycle: " + ex.ToString());
}
finally
{
	if (host != null)
	{
		if (host.State != CommunicationState.Closed) host.Close();
	}
}
Service Hosting in a Windows Service

The same configuration procedure for sef-hosting must be followed.

Service Hosting on IIS: ASP.NET Compatibility

The ASP.NET HTTP runtime is coupled to the IIS/ASP.NET hosting environment and HTTP communication.

The WCF Model is designed to behave consistently across hosting environments both inside and outside of IIS).

The ASP.NET HTTP runtime handles ASP.NET requests but does not participate in the processing of requests destined for WCF services hosted on IIS.
Instead, the WCF Service Model intercepts messages addressed to WCF services when the event “PostAuthenticateRequest” is raised and routes them through the WCF transport/channel stack.

Refer to this MSDN article for details.

Due to this default configuration:

1) The WCF model don’t have by access to some of the ASP.NET infrastructures: HttpContext.Current is always NULL when accessed from within a WCF service;

2) The HttpModule extensibility does not appply to WCF services requests: Once intercepted by the WCF hosting infrastructure, when the PostAuthenticateRequest event is raised, processing is NOT returned to the ASP.NET HTTP pipeline – Modules that are coded to intercept requests at later stages of the pipeline are NOT executed on WCF requests.

If required, WCF Services hosted on IIS and that communicate through HTTP transport protocol can run in ASP.NET compatibiliy mode and therfore:

1) WCF requests participate in the entire in the ASP.NET HTTP request lifecycle: WCF services use the HTTP pipeline through an IHttpHandler implementation, similar to the way requests for ASPX pages and ASMX Web services are handled;

2) WCF model can access ASP.NET infrastructures: HttpContext.Current exists and can be accessed from within a WCF service;

3) The HttpModule extensibility apply to WCF services requests: Modules can be executed on WCF requests both before and after service invocation; and

4) WCF services can share AppDomain state with ASP.NET (including static variables, events, etc).

How to do it

Enable AS.NET Compatibility mode

The compatibility mode is enabled at the application level through the following configuration in the application’s Web.config file:

<system.serviceModel>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />    </system.serviceModel>

Indidual services can use the AspNetCompatibilityRequirementsAttribute to indicate whether they support ASP.NET Compatibility Mode. The default value for this attribute is “Allowed”:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class wcfTestService : IwcfTestService
{
   // implementation
}
WCF ReST Error Handling

Exceptions in ReST services are represented by a HTTP status code >= 400 and can be supported by throwing a WebFaultException.

Because the WebFaultException derives from the regular FaultException, it exists in two versions: a non generic version and a generic version:

  • The non generic version only supports setting the HTTP status code;
  • while

  • The generic version allows to specify a DataContract class that should be send to the client.

How to do it

The error description class:

[DataContract]
public class ErrorInformation
{
    [DataMember]
    public bool Result { get; set; }
    [DataMember]
    public string ErrorLevel1 { get; set; }
    [DataMember]
    public string ErrorLevel2 { get; set; }
}

The interface of the service:

[ServiceContract]
public interface ITestService
{
  [WebGet(ResponseFormat = WebMessageFormat.Xml)]
  [OperationContract]
  void DoWork();
}

The service implementation:

public class TestService : ITestService
{
  public void DoWork()
  {

    try
    {
      // Do something
    }
    catch (Exception ex)
    {
      ErrorInformation _error = new ErrorInformation()
      _error.Result = false;
      _error.ErrorLevel1 = "Internal server error.";
      _error.ErrorLevel2 = ex.ToString();
     throw new WebFaultException<ErrorInformation>(_error, HttpStatusCode.Unauthorized);
     }
}

In this sample the server would return a 401 error code and the body is the defined DataContract in XML format.

References

[1] Refer to this article for how to implement a custom Help page