Tutorial: Azure WCF Relay REST tutorial
This tutorial describes how to build an Azure Relay host application that exposes a REST-based interface. REST enables a web client, such as a web browser, to access the Service Bus APIs through HTTP requests.
The tutorial uses the Windows Communication Foundation (WCF) REST programming model to construct a REST service on Azure Relay. For more information, see WCF REST Programming Model and Designing and Implementing Services.
You do the following tasks in this tutorial:
- Install prerequisites for this tutorial.
- Create a Relay namespace.
- Define a REST-based WCF service contract.
- Implement the REST-based WCF contract.
- Host and run the REST-based WCF service.
- Run and test the service.
Prerequisites
To complete this tutorial, you need the following prerequisites:
- An Azure subscription. If you don't have one, create a trial subscription before you begin.
- Visual Studio 2015 or later. The examples in this tutorial use Visual Studio 2019.
- Azure SDK for .NET. Install it from the SDK downloads page.
Create a Relay namespace
To begin using the relay features in Azure, you must first create a service namespace. A namespace provides a scoping container for addressing Azure resources within your application. Follow the instructions here to create a Relay namespace.
Define a REST-based WCF service contract to use with Azure Relay
When you create a WCF REST-style service, you must define the contract. The contract specifies what operations the host supports. A service operation resembles a web service method. Define a contract with a C++, C#, or Visual Basic interface. Each method in the interface corresponds to a specific service operation. Apply the ServiceContractAttribute attribute to each interface, and apply the OperationContractAttribute attribute to each operation.
Tip
If a method in an interface that has the ServiceContractAttribute doesn't have the OperationContractAttribute, that method isn't exposed. The code used for these tasks appears in the example following the procedure.
The primary difference between a WCF contract and a REST-style contract is the addition of a property to the OperationContractAttribute: WebGetAttribute. This property enables you to map a method in your interface to a method on the other side of the interface. This example uses the WebGetAttribute attribute to link a method to HTTP GET
. This approach enables Service Bus to accurately retrieve and interpret commands sent to the interface.
To create a contract with an interface
Start Microsoft Visual Studio as an administrator. To do so, right-click the Visual Studio program icon, and select Run as administrator.
In Visual Studio, select Create a new project.
In Create a new project, choose Console App (.NET Framework) for C# and select Next.
Name the project ImageListener. Use the default Location, and then select Create.
For a C# project, Visual Studio creates a Program.cs file. This class contains an empty
Main()
method, required for a console application project to build correctly.In Solution Explorer, right-click the ImageListener project, then select Manage NuGet Packages.
Select Browse, then search for and choose WindowsAzure.ServiceBus. Select Install, and accept the terms of use.
This step adds references to Service Bus and System.ServiceModel.dll. This package automatically adds references to the Service Bus libraries and the WCF
System.ServiceModel
.Explicitly add a reference to
System.ServiceModel.Web.dll
to the project. In Solution Explorer, right-click References under the project folder, and select Add Reference.In Add Reference, select Framework and enter System.ServiceModel.Web in Search. Select the System.ServiceModel.Web check box, then select OK.
Next, make the following code changes to the project:
Add the following
using
statements at the top of the Program.cs file.using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Web; using System.IO;
- System.ServiceModel is the namespace that enables programmatic access to basic features of WCF. WCF Relay uses many of the objects and attributes of WCF to define service contracts. You use this namespace in most of your relay applications.
- System.ServiceModel.Channels helps define the channel, which is the object through which you communicate with Azure Relay and the client web browser.
- System.ServiceModel.Web contains the types that enable you to create web-based applications.
Rename the
ImageListener
namespace toMicrosoft.ServiceBus.Samples
.namespace Microsoft.ServiceBus.Samples { ...
Directly after the opening curly brace of the namespace declaration, define a new interface named
IImageContract
and apply theServiceContractAttribute
attribute to the interface with a value ofhttps://samples.microsoft.com/ServiceModel/Relay/RESTTutorial1
.[ServiceContract(Name = "ImageContract", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/RESTTutorial1")] public interface IImageContract { }
The namespace value differs from the namespace that you use throughout the scope of your code. The namespace value is a unique identifier for this contract, and should have version information. For more information, see Service Versioning. Specifying the namespace explicitly prevents the default namespace value from being added to the contract name.
Within the
IImageContract
interface, declare a method for the single operation that theIImageContract
contract exposes in the interface and apply theOperationContract
attribute to the method that you want to expose as part of the public Service Bus contract.public interface IImageContract { [OperationContract] Stream GetImage(); }
In the
OperationContract
attribute, add theWebGet
value.public interface IImageContract { [OperationContract, WebGet] Stream GetImage(); }
Adding the
WebGet
value enables the relay service to route HTTP GET requests toGetImage
, and to translate the return values ofGetImage
into anHTTP GETRESPONSE
reply. Later in the tutorial, you'll use a web browser to access this method, and to display the image in the browser.Directly after the
IImageContract
definition, declare a channel that inherits from both theIImageContract
andIClientChannel
interfaces.public interface IImageChannel : IImageContract, IClientChannel { }
A channel is the WCF object through which the service and client pass information to each other. Later, you create the channel in your host application. Azure Relay then uses this channel to pass the HTTP GET requests from the browser to your
GetImage
implementation. The relay also uses the channel to take theGetImage
return value and translate it into anHTTP GETRESPONSE
for the client browser.Select Build > Build Solution to confirm the accuracy of your work so far.
Example that defines a WCF Relay contract
The following code shows a basic interface that defines a WCF Relay contract.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Web;
using System.IO;
namespace Microsoft.ServiceBus.Samples
{
[ServiceContract(Name = "IImageContract", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")]
public interface IImageContract
{
[OperationContract, WebGet]
Stream GetImage();
}
public interface IImageChannel : IImageContract, IClientChannel { }
class Program
{
static void Main(string[] args)
{
}
}
}
Implement the REST-based WCF service contract
To create a REST-style WCF Relay service, first create the contract by using an interface. The next step is to implement the interface. This procedure involves creating a class named ImageService
that implements the user-defined IImageContract
interface. After you implement the contract, you then configure the interface by using an App.config file. The configuration file contains necessary information for the application. This information includes the name of the service, the name of the contract, and the type of protocol that is used to communicate with the relay service. The code used for these tasks appears in the example following the procedure.
As with the previous steps, there's little difference between implementing a REST-style contract and a WCF Relay contract.
To implement a REST-style Service Bus contract
Create a new class named
ImageService
directly after the definition of theIImageContract
interface. TheImageService
class implements theIImageContract
interface.class ImageService : IImageContract { }
Similar to other interface implementations, you can implement the definition in a different file. However, for this tutorial, the implementation appears in the same file as the interface definition and
Main()
method.Apply the ServiceBehaviorAttribute attribute to the
IImageService
class to indicate that the class is an implementation of a WCF contract.[ServiceBehavior(Name = "ImageService", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")] class ImageService : IImageContract { }
As mentioned previously, this namespace isn't a traditional namespace. It's part of the WCF architecture that identifies the contract. For more information, see the Data Contract Names.
Add a .jpg image to your project. This file is a picture that the service displays in the receiving browser.
- Right-click your project and select Add.
- Then select Existing Item.
- Use Add Existing Item to browse to an appropriate .jpg, and then select Add. When adding the file, select All Files from the drop-down list next to File name.
The rest of this tutorial assumes that the name of the image is image.jpg. If you have a different file, you must rename the image, or change your code to compensate.
To make sure that the running service can find the image file, in Solution Explorer right-click the image file, then choose Properties. In Properties, set Copy to Output Directory to Copy if newer.
Use the procedure in To create a contract with an interface to add a reference to the System.Drawing.dll assembly to the project.
Add the following associated
using
statements:using System.Drawing; using System.Drawing.Imaging; using Microsoft.ServiceBus; using Microsoft.ServiceBus.Web;
In the
ImageService
class, add the following constructor that loads the bitmap and prepares to send it to the client browser:class ImageService : IImageContract { const string imageFileName = "image.jpg"; Image bitmap; public ImageService() { this.bitmap = Image.FromFile(imageFileName); } }
Directly after the previous code, add the following
GetImage
method in theImageService
class to return an HTTP message that contains the image.public Stream GetImage() { MemoryStream stream = new MemoryStream(); this.bitmap.Save(stream, ImageFormat.Jpeg); stream.Position = 0; WebOperationContext.Current.OutgoingResponse.ContentType = "image/jpeg"; return stream; }
This implementation uses
MemoryStream
to retrieve the image and prepare it for streaming to the browser. It starts the stream position at zero, declares the stream content as a .jpg, and streams the information.Select Build > Build Solution.
To define the configuration for running the web service on Service Bus
In Solution Explorer, double-click App.config to open the file in the Visual Studio editor.
The App.config file includes the service name, endpoint, and binding. The endpoint is the location Azure Relay exposes for clients and hosts to communicate with each other. The binding is the type of protocol that is used to communicate. The main difference here's that the configured service endpoint refers to a WebHttpRelayBinding binding.
The
<system.serviceModel>
XML element is a WCF element that defines one or more services. Here, it's used to define the service name and endpoint. At the bottom of the<system.serviceModel>
element, but still within<system.serviceModel>
, add a<bindings>
element that has the following content:<bindings> <!-- Application Binding --> <webHttpRelayBinding> <binding name="default"> <security relayClientAuthenticationType="None" /> </binding> </webHttpRelayBinding> </bindings>
This content defines the bindings used in the application. You can define multiple bindings, but for this tutorial you're defining only one.
The previous code defines a WCF Relay WebHttpRelayBinding binding with
relayClientAuthenticationType
set toNone
. This setting indicates that an endpoint using this binding doesn't require a client credential.After the
<bindings>
element, add a<services>
element. Similar to the bindings, you can define multiple services in a single configuration file. However, for this tutorial, you define only one.<services> <!-- Application Service --> <service name="Microsoft.ServiceBus.Samples.ImageService" behaviorConfiguration="default"> <endpoint name="RelayEndpoint" contract="Microsoft.ServiceBus.Samples.IImageContract" binding="webHttpRelayBinding" bindingConfiguration="default" behaviorConfiguration="sbTokenProvider" address="" /> </service> </services>
This content configures a service that uses the previously defined default
webHttpRelayBinding
. It also uses the defaultsbTokenProvider
, which is defined in the next step.After the
<services>
element, create a<behaviors>
element with the following content, replacingSAS_KEY
with the Shared Access Signature (SAS) key. To obtain an SAS key from the Azure portal, see Get management credentials.<behaviors> <endpointBehaviors> <behavior name="sbTokenProvider"> <transportClientEndpointBehavior> <tokenProvider> <sharedAccessSignature keyName="RootManageSharedAccessKey" key="YOUR_SAS_KEY" /> </tokenProvider> </transportClientEndpointBehavior> </behavior> </endpointBehaviors> <serviceBehaviors> <behavior name="default"> <serviceDebug httpHelpPageEnabled="false" httpsHelpPageEnabled="false" /> </behavior> </serviceBehaviors> </behaviors>
Still in App.config, in the
<appSettings>
element, replace the entire connection string value with the connection string you previously obtained from the portal.<appSettings> <!-- Service Bus specific app settings for messaging connections --> <add key="Microsoft.ServiceBus.ConnectionString" value="Endpoint=sb://yourNamespace.servicebus.chinacloudapi.cn/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=YOUR_SAS_KEY"/> </appSettings>
Select Build > Build Solution to build the entire solution.
Example that implements the REST-based WCF service contract
The following code shows the contract and service implementation for a REST-based service that is running on Service Bus using the WebHttpRelayBinding
binding.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Web;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using Microsoft.ServiceBus;
using Microsoft.ServiceBus.Web;
namespace Microsoft.ServiceBus.Samples
{
[ServiceContract(Name = "ImageContract", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")]
public interface IImageContract
{
[OperationContract, WebGet]
Stream GetImage();
}
public interface IImageChannel : IImageContract, IClientChannel { }
[ServiceBehavior(Name = "ImageService", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")]
class ImageService : IImageContract
{
const string imageFileName = "image.jpg";
Image bitmap;
public ImageService()
{
this.bitmap = Image.FromFile(imageFileName);
}
public Stream GetImage()
{
MemoryStream stream = new MemoryStream();
this.bitmap.Save(stream, ImageFormat.Jpeg);
stream.Position = 0;
WebOperationContext.Current.OutgoingResponse.ContentType = "image/jpeg";
return stream;
}
}
class Program
{
static void Main(string[] args)
{
}
}
}
The following example shows the App.config file associated with the service.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
</startup>
<system.serviceModel>
<extensions>
<!-- In this extension section we are introducing all known service bus extensions. User can remove the ones they don't need. -->
<behaviorExtensions>
<add name="connectionStatusBehavior"
type="Microsoft.ServiceBus.Configuration.ConnectionStatusElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add name="transportClientEndpointBehavior"
type="Microsoft.ServiceBus.Configuration.TransportClientEndpointBehaviorElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add name="serviceRegistrySettings"
type="Microsoft.ServiceBus.Configuration.ServiceRegistrySettingsElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</behaviorExtensions>
<bindingElementExtensions>
<add name="netMessagingTransport"
type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingTransportExtensionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add name="tcpRelayTransport"
type="Microsoft.ServiceBus.Configuration.TcpRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add name="httpRelayTransport"
type="Microsoft.ServiceBus.Configuration.HttpRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add name="httpsRelayTransport"
type="Microsoft.ServiceBus.Configuration.HttpsRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add name="onewayRelayTransport"
type="Microsoft.ServiceBus.Configuration.RelayedOnewayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</bindingElementExtensions>
<bindingExtensions>
<add name="basicHttpRelayBinding"
type="Microsoft.ServiceBus.Configuration.BasicHttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add name="webHttpRelayBinding"
type="Microsoft.ServiceBus.Configuration.WebHttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add name="ws2007HttpRelayBinding"
type="Microsoft.ServiceBus.Configuration.WS2007HttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add name="netTcpRelayBinding"
type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add name="netOnewayRelayBinding"
type="Microsoft.ServiceBus.Configuration.NetOnewayRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add name="netEventRelayBinding"
type="Microsoft.ServiceBus.Configuration.NetEventRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add name="netMessagingBinding"
type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</bindingExtensions>
</extensions>
<bindings>
<!-- Application Binding -->
<webHttpRelayBinding>
<binding name="default">
<security relayClientAuthenticationType="None" />
</binding>
</webHttpRelayBinding>
</bindings>
<services>
<!-- Application Service -->
<service name="Microsoft.ServiceBus.Samples.ImageService"
behaviorConfiguration="default">
<endpoint name="RelayEndpoint"
contract="Microsoft.ServiceBus.Samples.IImageContract"
binding="webHttpRelayBinding"
bindingConfiguration="default"
behaviorConfiguration="sbTokenProvider"
address="" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="sbTokenProvider">
<transportClientEndpointBehavior>
<tokenProvider>
<sharedAccessSignature keyName="RootManageSharedAccessKey" key="YOUR_SAS_KEY" />
</tokenProvider>
</transportClientEndpointBehavior>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="default">
<serviceDebug httpHelpPageEnabled="false" httpsHelpPageEnabled="false" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<appSettings>
<!-- Service Bus specific app settings for messaging connections -->
<add key="Microsoft.ServiceBus.ConnectionString"
value="Endpoint=sb://yourNamespace.servicebus.chinacloudapi.cn/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=<YOUR_SAS_KEY>"/>
</appSettings>
</configuration>
Host the REST-based WCF service to use Azure Relay
This section describes how to run a web service using a console application with WCF Relay. A complete listing of the code written in this section appears in the example following the procedure.
To create a base address for the service
In the
Main()
function declaration, create a variable to store the namespace of your project. Make sure to replaceyourNamespace
with the name of the Relay namespace you created previously.string serviceNamespace = "yourNamespace";
Service Bus uses the name of your namespace to create a unique URI.
Create a
Uri
instance for the base address of the service that is based on the namespace.Uri address = ServiceBusEnvironment.CreateServiceUri("https", serviceNamespace, "Image");
To create and configure the web service host
Still in Main()
, create the web service host, using the URI address created earlier in this section.
WebServiceHost host = new WebServiceHost(typeof(ImageService), address);
The service host is the WCF object that instantiates the host application. This example passes it the type of host you want to create, which is an ImageService
, and also the address at which you want to expose the host application.
To run the web service host
Still in
Main()
, add the following line to open the service.host.Open();
The service is now running.
Display a message indicating that the service is running, and how to stop the service.
Console.WriteLine("Copy the following address into a browser to see the image: "); Console.WriteLine(address + "GetImage"); Console.WriteLine(); Console.WriteLine("Press [Enter] to exit"); Console.ReadLine();
When finished, close the service host.
host.Close();
Example of the service contract and implementation
The following example includes the service contract and implementation from previous steps in the tutorial and hosts the service in a console application. Compile the following code into an executable named ImageListener.exe.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Web;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using Microsoft.ServiceBus;
using Microsoft.ServiceBus.Web;
namespace Microsoft.ServiceBus.Samples
{
[ServiceContract(Name = "ImageContract", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")]
public interface IImageContract
{
[OperationContract, WebGet]
Stream GetImage();
}
public interface IImageChannel : IImageContract, IClientChannel { }
[ServiceBehavior(Name = "ImageService", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")]
class ImageService : IImageContract
{
const string imageFileName = "image.jpg";
Image bitmap;
public ImageService()
{
this.bitmap = Image.FromFile(imageFileName);
}
public Stream GetImage()
{
MemoryStream stream = new MemoryStream();
this.bitmap.Save(stream, ImageFormat.Jpeg);
stream.Position = 0;
WebOperationContext.Current.OutgoingResponse.ContentType = "image/jpeg";
return stream;
}
}
class Program
{
static void Main(string[] args)
{
string serviceNamespace = "InsertServiceNamespaceHere";
Uri address = ServiceBusEnvironment.CreateServiceUri("https", serviceNamespace, "Image");
WebServiceHost host = new WebServiceHost(typeof(ImageService), address);
host.Open();
Console.WriteLine("Copy the following address into a browser to see the image: ");
Console.WriteLine(address + "GetImage");
Console.WriteLine();
Console.WriteLine("Press [Enter] to exit");
Console.ReadLine();
host.Close();
}
}
}
Run and test the service
After building the solution, do the following to run the application:
- Select F5, or browse to the executable file location, ImageListener\bin\Debug\ImageListener.exe, to run the service. Keep the app running, because it's required for the next step.
- Copy and paste the address from the command prompt into a browser to see the image.
- When you're finished, select Enter in the command prompt window to close the app.
Next steps
Now that you've built an application that uses the Azure Relay service, see the following articles to learn more: