教程:Azure WCF 中继 REST 教程Tutorial: Azure WCF Relay REST tutorial

本教程介绍如何生成 Azure 中继主机应用程序,用于公开基于 REST 的接口。This tutorial describes how to build an Azure Relay host application that exposes a REST-based interface. REST 使 Web 客户端(例如 Web 浏览器)可通过 HTTP 请求访问服务总线 API。REST enables a web client, such as a web browser, to access the Service Bus APIs through HTTP requests.

该教程使用 Windows Communication Foundation (WCF) REST 编程模型在 Azure 中继上构建 REST 服务。The tutorial uses the Windows Communication Foundation (WCF) REST programming model to construct a REST service on Azure Relay. 有关详细信息,请参阅 WCF REST 编程模型设计和实现服务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.
  • 定义基于 REST 的 WCF 服务协定。Define a REST-based WCF service contract.
  • 实现基于 REST 的 WCF 协定。Implement the REST-based WCF contract.
  • 托管并运行基于 REST 的 WCF 服务。Host and run the REST-based WCF service.
  • 运行和测试服务。Run and test the service.

先决条件Prerequisites

若要完成本教程,需要具备以下先决条件:To complete this tutorial, you need the following prerequisites:

创建中继命名空间Create a Relay namespace

若要开始在 Azure 中使用中继功能,必须先创建一个服务命名空间。To begin using the relay features in Azure, you must first create a service namespace. 命名空间提供了用于对应用程序中的 Azure 资源进行寻址的范围容器。A namespace provides a scoping container for addressing Azure resources within your application. 请按照此处的说明创建中继命名空间。Follow the instructions here to create a Relay namespace.

定义基于 REST 的 WCF 服务约定以用于 Azure 中继Define a REST-based WCF service contract to use with Azure Relay

创建创建 WCF REST 样式的服务时,必须定义约定。When you create a WCF REST-style service, you must define the contract. 约定指定主机支持的操作。The contract specifies what operations the host supports. 服务操作类似于 Web 服务方法。A service operation resembles a web service method. 使用 C++、C# 或 Visual Basic 接口定义协定。Define a contract with a C++, C#, or Visual Basic interface. 接口中的每个方法都对应一个特定的服务操作。Each method in the interface corresponds to a specific service operation. ServiceContractAttribute 属性应用到每个接口,将 OperationContractAttribute 属性应用到每个操作。Apply the ServiceContractAttribute attribute to each interface, and apply the OperationContractAttribute attribute to each operation.

提示

如果具有 ServiceContractAttribute 的接口中的方法没有 OperationContractAttribute,则该方法是不公开的。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.

WCF 协定和 REST 样式的协定的主要区别在于是否向 OperationContractAttribute 添加一个属性:WebGetAttributeThe 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. 此示例使用 WebGetAttribute 属性将一个方法链接到 HTTP GETThis 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

  1. 以管理员身份启动 Microsoft Visual Studio。Start Microsoft Visual Studio as an administrator. 为此,请右键单击 Visual Studio 程序图标,并选择“以管理员身份运行”。To do so, right-click the Visual Studio program icon, and select Run as administrator.

  2. 在 Visual Studio 中选择“创建新项目”。In Visual Studio, select Create a new project.

  3. 在“创建新项目”中,选择适用于 C# 的“控制台应用(.NET Framework)”,然后选择“下一步”。In Create a new project, choose Console App (.NET Framework) for C# and select Next.

  4. 将项目命名为 ImageListenerName the project ImageListener. 使用默认的位置,然后选择“创建”。Use the default Location, and then select Create.

    对于 C# 项目,Visual Studio 会创建 Program.cs 文件。For a C# project, Visual Studio creates a Program.cs file. 此类包含一个空的 Main() 方法,需要此方法才能正确生成控制台应用程序项目。This class contains an empty Main() method, required for a console application project to build correctly.

  5. 在“解决方案资源管理器”中,右键单击“ImageListener”项目,并选择“管理 NuGet 包” 。In Solution Explorer, right-click the ImageListener project, then select Manage NuGet Packages.

  6. 选择“浏览”,然后搜索并选择“WindowsAzure.ServiceBus”。Select Browse, then search for and choose WindowsAzure.ServiceBus. 选择“安装”并接受使用条款。Select Install, and accept the terms of use.

    此步骤添加对服务总线和 System.ServiceModel.dll 的引用。This step adds references to Service Bus and System.ServiceModel.dll. 该包自动添加对服务总线库和 WCF System.ServiceModel 的引用。This package automatically adds references to the Service Bus libraries and the WCF System.ServiceModel.

  7. 将对 System.ServiceModel.Web.dll 的引用显式添加到项目中。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.

  8. 在“添加引用”中选择“框架”,然后在“搜索”中输入 System.ServiceModel.WebIn Add Reference, select Framework and enter System.ServiceModel.Web in Search. 选择“System.ServiceModel.Web”复选框,并单击“确定”。Select the System.ServiceModel.Web check box, then click OK.

接下来,对项目进行以下代码更改:Next, make the following code changes to the project:

  1. Program.cs 文件顶部添加以下 using 语句。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 命名空间,可以编程方式访问 WCF 的基本功能。System.ServiceModel is the namespace that enables programmatic access to basic features of WCF. WCF 中继使用 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 可帮助定义通道,通道是用来与 Azure 中继和客户端 Web 浏览器通信的对象。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 包含的类型可用于创建基于 Web 的应用程序。System.ServiceModel.Web contains the types that enable you to create web-based applications.
  2. ImageListener 命名空间重命名为 Microsoft.ServiceBus.SamplesRename the ImageListener namespace to Microsoft.ServiceBus.Samples.

    namespace Microsoft.ServiceBus.Samples
    {
        ...
    
  3. 在命名空间声明的左大括号后面,紧接着定义一个名为 IImageContract 的新接口,然后将 ServiceContractAttribute 属性应用于该接口,其值为 https://samples.microsoft.com/ServiceModel/Relay/RESTTutorial1Directly after the opening curly brace of the namespace declaration, define a new interface named IImageContract and apply the ServiceContractAttribute attribute to the interface with a value of https://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.

  4. IImageContract 接口中,为 IImageContract 协定在接口中公开的单个操作声明一个方法,并将 OperationContract 属性应用到希望将其作为公共服务总线协定的一部分进行公开的方法中。Within the IImageContract interface, declare a method for the single operation that the IImageContract contract exposes in the interface and apply the OperationContract attribute to the method that you want to expose as part of the public Service Bus contract.

    public interface IImageContract
    {
        [OperationContract]
        Stream GetImage();
    }
    
  5. OperationContract 属性中添加 WebGet 值。In the OperationContract attribute, add the WebGet value.

    public interface IImageContract
    {
        [OperationContract, WebGet]
        Stream GetImage();
    }
    

    添加 WebGet 值可以让中继服务将 HTTP GET 请求路由到 GetImage,并将 GetImage 的返回值转换为 HTTP GETRESPONSE 回复。Adding the WebGet value enables the relay service to route HTTP GET requests to GetImage, and to translate the return values of GetImage into an HTTP GETRESPONSE reply. 稍后在本教程中,将使用 Web 浏览器访问此方法,并会在浏览器中显示图像。Later in the tutorial, you'll use a web browser to access this method, and to display the image in the browser.

  6. 直接在 IImageContract 定义的后面,声明从 IImageContractIClientChannel 接口继承的通道。Directly after the IImageContract definition, declare a channel that inherits from both the IImageContract and IClientChannel interfaces.

    public interface IImageChannel : IImageContract, IClientChannel { }
    

    通道是服务和客户端用来互相传递信息的 WCF 对象。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 中继将使用该通道将浏览器的 HTTP GET 请求传递到 GetImage 实现。Azure Relay then uses this channel to pass the HTTP GET requests from the browser to your GetImage implementation. 中继还使用该通道获取 GetImage 返回值并将其转换为客户端浏览器的 HTTP GETRESPONSEThe relay also uses the channel to take the GetImage return value and translate it into an HTTP GETRESPONSE for the client browser.

  7. 选择“生成” > “生成解决方案”,以确认到目前为止操作的准确性 。Select Build > Build Solution to confirm the accuracy of your work so far.

定义 WCF 中继协定的示例Example that defines a WCF Relay contract

以下代码显示了定义 WCF 中继协定的基本接口。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)
        {
        }
    }
}

实现基于 REST 的 WCF 服务协定Implement the REST-based WCF service contract

若要创建 REST 样式的 WCF 中继服务,请先使用接口创建协定。To create a REST-style WCF Relay service, first create the contract by using an interface. 下一步是实现该接口。The next step is to implement the interface. 此过程包括创建名为 ImageService 的类,用于实现用户定义的 IImageContract 接口。This procedure involves creating a class named ImageService that implements the user-defined IImageContract interface. 实现协定后,可以使用 App.config 文件配置接口。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.

与前面的步骤一样,实现 REST 样式的协定与实现 WCF 中继协定之间的差别很小。As with the previous steps, there's little difference between implementing a REST-style contract and a WCF Relay contract.

实现 REST 样式的服务总线约定To implement a REST-style Service Bus contract

  1. IImageContract 接口定义的正下方创建名为 ImageService 的新类。Create a new class named ImageService directly after the definition of the IImageContract interface. ImageService 类实现 IImageContract 接口。The ImageService class implements the IImageContract interface.

    class ImageService : IImageContract
    {
    }
    

    与其他接口实现类似,可以在另一个文件中实现定义。Similar to other interface implementations, you can implement the definition in a different file. 但是,在本教程中,实现所在的文件与接口定义和 Main() 方法所在的文件相同。However, for this tutorial, the implementation appears in the same file as the interface definition and Main() method.

  2. ServiceBehaviorAttribute 属性应用到 IImageService 类,以指示该类是 WCF 协定的实现。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. 而是用于标识协定的 WCF 体系结构的一部分。It's part of the WCF architecture that identifies the contract. 有关详细信息,请参阅数据协定名称For more information, see the Data Contract Names.

  3. 将一幅 .jpg 图像添加到项目中。Add a .jpg image to your project. 此文件是服务在接收浏览器中显示的图片。This file is a picture that the service displays in the receiving browser.

    1. 右键单击项目,并选择“添加”。Right-click your project and select Add.
    2. 然后选择“现有项”。Then select Existing Item.
    3. 使用“添加现有项”浏览到相应的 .jpg,然后选择“添加”。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.

    本教程的余下部分假定图像的名称为 image.jpgThe rest of this tutorial assumes that the name of the image is image.jpg. 如果 .jpg 文件名不是这样,则必须重命名图像,或更改代码进行弥补。If you have a different file, you must rename the image, or change your code to compensate.

  4. 为确保正在运行的服务可以找到该图像文件,请在“解决方案资源管理器”中右键单击该图像文件,并选择“属性”。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.

  5. 使用使用接口创建协定中的过程,将对 System.Drawing.dll 程序集的引用添加到项目中。Use the procedure in To create a contract with an interface to add a reference to the System.Drawing.dll assembly to the project.

  6. 添加以下关联的 using 语句:Add the following associated using statements:

    using System.Drawing;
    using System.Drawing.Imaging;
    using Microsoft.ServiceBus;
    using Microsoft.ServiceBus.Web;
    
  7. ImageService 类中定义以下构造函数,以便加载位图并准备将该位图发送到客户端浏览器: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);
        }
    }
    
  8. 直接在上一代码后面,在 ImageService 类中添加以下 GetImage 方法,以返回包含该映像的 HTTP 消息。Directly after the previous code, add the following GetImage method in the ImageService 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;
    }
    

    此实现使用 MemoryStream 检索映像并准备将其流式传输到浏览器。This implementation uses MemoryStream to retrieve the image and prepare it for streaming to the browser. 它将流位置设置为从零开始,将流内容声明为 .jpg,然后流式传输信息。It starts the stream position at zero, declares the stream content as a .jpg, and streams the information.

  9. 选择“生成” > “生成解决方案” 。Select Build > Build Solution.

定义配置以便在服务总线上运行 Web 服务To define the configuration for running the web service on Service Bus

  1. 在“解决方案资源管理器”中,双击“App.config”在 Visual Studio 编辑器中将其打开。In Solution Explorer, double-click App.config to open the file in the Visual Studio editor.

    App.config 文件包含服务名称、终结点和绑定。The App.config file includes the service name, endpoint, and binding. 终结点是 Azure 中继公开的、让客户端和主机相互通信的位置。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. 此处的主要差别在于,配置的服务终结点是指 WebHttpRelayBinding 绑定。The main difference here is that the configured service endpoint refers to a WebHttpRelayBinding binding.

  2. <system.serviceModel> XML 元素是一个 WCF 元素,用于定义一个或多个服务。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. <system.serviceModel> 元素的下面(仍在 <system.serviceModel> 中)添加具有以下内容的 <bindings> 元素: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.

    前面的代码定义了一个 WCF 中继 WebHttpRelayBinding 绑定,其中 relayClientAuthenticationType 设置为 NoneThe previous code defines a WCF Relay WebHttpRelayBinding binding with relayClientAuthenticationType set to None. 此设置表明使用此绑定的终结点将不需要客户端凭据。This setting indicates that an endpoint using this binding doesn't require a client credential.

  3. <bindings> 元素后面添加 <services> 元素。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>
    

    此内容将配置一个服务,该服务使用前面定义的默认 webHttpRelayBindingThis content configures a service that uses the previously defined default webHttpRelayBinding. 此外,它还使用下一步骤中定义的默认 sbTokenProviderIt also uses the default sbTokenProvider, which is defined in the next step.

  4. <services> 元素的后面,使用以下内容创建 <behaviors> 元素,并将 SAS_KEY 替换为共享访问签名 (SAS) 密钥。After the <services> element, create a <behaviors> element with the following content, replacing SAS_KEY with the Shared Access Signature (SAS) key. 若要从 Azure 门户获取 SAS 密钥,请参阅获取管理凭据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>
    
  5. 仍在 App.config 文件中,在 <appSettings> 元素中,将整个连接字符串替换为以前从门户获取的连接字符串。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>
    
  6. 选择“生成” > “生成解决方案”以生成整个解决方案。Select Build > Build Solution to build the entire solution.

实现基于 REST 的 WCF 服务协定的示例Example that implements the REST-based WCF service contract

以下代码演示了一个在服务总线上运行并使用 WebHttpRelayBinding 绑定的、基于 REST 的服务的协定和服务实现。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)
        {
        }
    }
}

以下示例显示了与该服务关联的 App.config 文件。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>

托管基于 REST 的 WCF 服务以使用 Azure 中继Host the REST-based WCF service to use Azure Relay

本部分介绍如何使用控制台应用程序通过 WCF 中继运行 Web 服务。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

  1. Main() 函数声明中,创建一个变量,用于存储项目的命名空间。In the Main() function declaration, create a variable to store the namespace of your project. 请确保将 yourNamespace 替换为之前创建的中继命名空间的名称。Make sure to replace yourNamespace with the name of the Relay namespace you created previously.

    string serviceNamespace = "yourNamespace";
    

    服务总线使用服务命名空间的名称来创建唯一 URI。Service Bus uses the name of your namespace to create a unique URI.

  2. 为基于服务命名空间的服务的基本地址创建 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");
    

创建并配置 Web 服务主机To create and configure the web service host

仍在 Main() 中,使用之前在本部分创建的 URI 地址创建 Web 服务主机。Still in Main(), create the web service host, using the URI address created earlier in this section.

WebServiceHost host = new WebServiceHost(typeof(ImageService), address);

该服务主机是可实例化主机应用程序的 WCF 对象。The service host is the WCF object that instantiates the host application. 本示例会将要创建的主机类型 (ImageService),以及要公开主机应用程序的地址传递给它。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.

运行 Web 服务主机To run the web service host

  1. 仍在 Main() 中,添加以下行以打开服务。Still in Main(), add the following line to open the service.

    host.Open();
    

    服务现在正在运行。The service is now running.

  2. 显示表明服务正在运行以及如何停止服务的消息。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();
    
  3. 完成后,关闭服务主机。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. 将以下代码编译到名为 ImageListener.exe 的可执行文件中。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:

  1. 按 F5,或浏览到可执行文件的位置 (ImageListener\bin\Debug\ImageListener.exe) 来运行此服务。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.
  2. 将命令提示符中的地址复制并粘贴到浏览器中以查看图像。Copy and paste the address from the command prompt into a browser to see the image.
  3. 完成后,在命令提示符窗口中按 Enter 关闭应用。When you're finished, select Enter in the command prompt window to close the app.

后续步骤Next steps

至此,你已使用 Azure 中继服务构建一个应用程序,接下来请参阅以下文章了解详细信息:Now that you've built an application that uses the Azure Relay service, see the following articles to learn more: