适用于 Azure Functions 的 Azure 服务总线输出绑定

使用 Azure 服务总线输出绑定发送队列或主题消息。

有关设置和配置详细信息,请参阅概述

重要

本文使用选项卡来支持多个版本的 Node.js 编程模型。 v4 模型目前处于预览状态,旨在为 JavaScript 和 TypeScript 开发人员提供更为灵活和直观的体验。 在升级指南中详细了解 v3 和 v4 之间的差异。

Azure Functions 支持两种 Python 编程模型。 定义绑定的方式取决于选择的编程模型。

使用 Python v2 编程模型,可以直接在 Python 函数代码中使用修饰器定义绑定。 有关详细信息,请参阅 Python 开发人员指南

本文同时支持两个编程模型。

示例

可以使用以下 C# 模式之一创建 C# 函数:

  • 进程内类库:编译的 C# 函数,该函数在与 Functions 运行时相同的进程中运行。
  • 独立进程类库:编译的 C# 函数,该函数在独立于运行时的进程中运行。 支持在 .NET 5.0 上运行的 C# 函数需要独立的进程。
  • C# 脚本:主要在 Azure 门户中创建 C# 函数时使用。

此代码定义并初始化 ILogger

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Threading.Tasks;
using Azure.Messaging.ServiceBus;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace SampleApp
{
    /// <summary>
    /// Samples demonstrating binding to the <see cref="ServiceBusReceivedMessage"/> type.
    /// </summary>
    public class ServiceBusReceivedMessageFunctions
    {
        //<docsnippet_servicebusmessage_createlogger>
        private readonly ILogger<ServiceBusReceivedMessageFunctions> _logger;

        public ServiceBusReceivedMessageFunctions(ILogger<ServiceBusReceivedMessageFunctions> logger)
        {
            _logger = logger;
        }
        //</docsnippet_servicebusmessage_createlogger>
        /// <summary>
        /// This function demonstrates binding to a single <see cref="ServiceBusReceivedMessage"/>.
        /// </summary>
        //<docsnippet_servicebus_readmessage>
        [Function(nameof(ServiceBusReceivedMessageFunction))]
        [ServiceBusOutput("outputQueue", Connection = "ServiceBusConnection")]
        public string ServiceBusReceivedMessageFunction(
            [ServiceBusTrigger("queue", Connection = "ServiceBusConnection")] ServiceBusReceivedMessage message)
        {
            _logger.LogInformation("Message ID: {id}", message.MessageId);
            _logger.LogInformation("Message Body: {body}", message.Body);
            _logger.LogInformation("Message Content-Type: {contentType}", message.ContentType);

            var outputMessage = $"Output message created at {DateTime.Now}";
            return outputMessage;
        }
        //</docsnippet_servicebus_readmessage>
        /// <summary>
        /// This function demonstrates binding to an array of <see cref="ServiceBusReceivedMessage"/>.
        /// Note that when doing so, you must also set the <see cref="ServiceBusTriggerAttribute.IsBatched"/> property
        /// to <value>true</value>.
        /// </summary>
        //<docsnippet_servicebus_readbatch>
        [Function(nameof(ServiceBusReceivedMessageBatchFunction))]
        public void ServiceBusReceivedMessageBatchFunction(
            [ServiceBusTrigger("queue", Connection = "ServiceBusConnection", IsBatched = true)] ServiceBusReceivedMessage[] messages)
        {
            foreach (ServiceBusReceivedMessage message in messages)
            {
                _logger.LogInformation("Message ID: {id}", message.MessageId);
                _logger.LogInformation("Message Body: {body}", message.Body);
                _logger.LogInformation("Message Content-Type: {contentType}", message.ContentType);
            }
        }
        //</docsnippet_servicebus_readbatch>
        /// <summary>
        /// This functions demonstrates that it is possible to bind to both the ServiceBusReceivedMessage and any of the supported binding contract
        /// properties at the same time. If attempting this, the ServiceBusReceivedMessage must be the first parameter. There is not
        /// much benefit to doing this as all of the binding contract properties are available as properties on the ServiceBusReceivedMessage.
        /// </summary>
        [Function(nameof(ServiceBusReceivedMessageWithStringProperties))]
        public void ServiceBusReceivedMessageWithStringProperties(
            [ServiceBusTrigger("queue", Connection = "ServiceBusConnection")]
            ServiceBusReceivedMessage message, string messageId, int deliveryCount)
        {
            // The MessageId property and the messageId parameter are the same.
            _logger.LogInformation("Message ID: {id}", message.MessageId);
            _logger.LogInformation("Message ID: {id}", messageId);

            // Similarly the DeliveryCount property and the deliveryCount parameter are the same.
            _logger.LogInformation("Delivery Count: {count}", message.DeliveryCount);
            _logger.LogInformation("Delivery Count: {count}", deliveryCount);
        }
        //<docsnippet_servicebus_message_actions>
        [Function(nameof(ServiceBusMessageActionsFunction))]
        public async Task ServiceBusMessageActionsFunction(
            [ServiceBusTrigger("queue", Connection = "ServiceBusConnection", AutoCompleteMessages = false)]
            ServiceBusReceivedMessage message,
            ServiceBusMessageActions messageActions)
        {
            _logger.LogInformation("Message ID: {id}", message.MessageId);
            _logger.LogInformation("Message Body: {body}", message.Body);
            _logger.LogInformation("Message Content-Type: {contentType}", message.ContentType);

            // Complete the message
            await messageActions.CompleteMessageAsync(message);
        }
        //</docsnippet_servicebus_message_actions>
    }
}

此示例演示一个 C# 函数,该函数接收消息并将其写入第二个队列:

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Threading.Tasks;
using Azure.Messaging.ServiceBus;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace SampleApp
{
    /// <summary>
    /// Samples demonstrating binding to the <see cref="ServiceBusReceivedMessage"/> type.
    /// </summary>
    public class ServiceBusReceivedMessageFunctions
    {
        //<docsnippet_servicebusmessage_createlogger>
        private readonly ILogger<ServiceBusReceivedMessageFunctions> _logger;

        public ServiceBusReceivedMessageFunctions(ILogger<ServiceBusReceivedMessageFunctions> logger)
        {
            _logger = logger;
        }
        //</docsnippet_servicebusmessage_createlogger>
        /// <summary>
        /// This function demonstrates binding to a single <see cref="ServiceBusReceivedMessage"/>.
        /// </summary>
        //<docsnippet_servicebus_readmessage>
        [Function(nameof(ServiceBusReceivedMessageFunction))]
        [ServiceBusOutput("outputQueue", Connection = "ServiceBusConnection")]
        public string ServiceBusReceivedMessageFunction(
            [ServiceBusTrigger("queue", Connection = "ServiceBusConnection")] ServiceBusReceivedMessage message)
        {
            _logger.LogInformation("Message ID: {id}", message.MessageId);
            _logger.LogInformation("Message Body: {body}", message.Body);
            _logger.LogInformation("Message Content-Type: {contentType}", message.ContentType);

            var outputMessage = $"Output message created at {DateTime.Now}";
            return outputMessage;
        }
        //</docsnippet_servicebus_readmessage>
        /// <summary>
        /// This function demonstrates binding to an array of <see cref="ServiceBusReceivedMessage"/>.
        /// Note that when doing so, you must also set the <see cref="ServiceBusTriggerAttribute.IsBatched"/> property
        /// to <value>true</value>.
        /// </summary>
        //<docsnippet_servicebus_readbatch>
        [Function(nameof(ServiceBusReceivedMessageBatchFunction))]
        public void ServiceBusReceivedMessageBatchFunction(
            [ServiceBusTrigger("queue", Connection = "ServiceBusConnection", IsBatched = true)] ServiceBusReceivedMessage[] messages)
        {
            foreach (ServiceBusReceivedMessage message in messages)
            {
                _logger.LogInformation("Message ID: {id}", message.MessageId);
                _logger.LogInformation("Message Body: {body}", message.Body);
                _logger.LogInformation("Message Content-Type: {contentType}", message.ContentType);
            }
        }
        //</docsnippet_servicebus_readbatch>
        /// <summary>
        /// This functions demonstrates that it is possible to bind to both the ServiceBusReceivedMessage and any of the supported binding contract
        /// properties at the same time. If attempting this, the ServiceBusReceivedMessage must be the first parameter. There is not
        /// much benefit to doing this as all of the binding contract properties are available as properties on the ServiceBusReceivedMessage.
        /// </summary>
        [Function(nameof(ServiceBusReceivedMessageWithStringProperties))]
        public void ServiceBusReceivedMessageWithStringProperties(
            [ServiceBusTrigger("queue", Connection = "ServiceBusConnection")]
            ServiceBusReceivedMessage message, string messageId, int deliveryCount)
        {
            // The MessageId property and the messageId parameter are the same.
            _logger.LogInformation("Message ID: {id}", message.MessageId);
            _logger.LogInformation("Message ID: {id}", messageId);

            // Similarly the DeliveryCount property and the deliveryCount parameter are the same.
            _logger.LogInformation("Delivery Count: {count}", message.DeliveryCount);
            _logger.LogInformation("Delivery Count: {count}", deliveryCount);
        }
        //<docsnippet_servicebus_message_actions>
        [Function(nameof(ServiceBusMessageActionsFunction))]
        public async Task ServiceBusMessageActionsFunction(
            [ServiceBusTrigger("queue", Connection = "ServiceBusConnection", AutoCompleteMessages = false)]
            ServiceBusReceivedMessage message,
            ServiceBusMessageActions messageActions)
        {
            _logger.LogInformation("Message ID: {id}", message.MessageId);
            _logger.LogInformation("Message Body: {body}", message.Body);
            _logger.LogInformation("Message Content-Type: {contentType}", message.ContentType);

            // Complete the message
            await messageActions.CompleteMessageAsync(message);
        }
        //</docsnippet_servicebus_message_actions>
    }
}

 


此示例使用带有 OutputType 对象的 HTTP 触发器来发送 HTTP 响应并写入输出消息。

[Function("HttpSendMsg")]
public async Task<OutputType> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req, FunctionContext context)
{
   _logger.LogInformation($"C# HTTP trigger function processed a request for {context.InvocationId}.");

   HttpResponseData response = req.CreateResponse(HttpStatusCode.OK);
   await response.WriteStringAsync("HTTP response: Message sent");

   return new OutputType()
   {
       OutputEvent = "MyMessage",
       HttpResponse = response
   };
}

此代码定义了多输出类型 OutputType,其中包括 OutputEvent 上的服务总线输出绑定定义:

 public class OutputType
{
   [ServiceBusOutput("TopicOrQueueName", Connection = "ServiceBusConnection")]
   public string OutputEvent { get; set; }

   public HttpResponseData HttpResponse { get; set; }
}

以下示例演示了一个 Java 函数,该函数在由 HTTP 请求触发时将消息发送到服务总线队列 myqueue

@FunctionName("httpToServiceBusQueue")
@ServiceBusQueueOutput(name = "message", queueName = "myqueue", connection = "AzureServiceBusConnection")
public String pushToQueue(
  @HttpTrigger(name = "request", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS)
  final String message,
  @HttpOutput(name = "response") final OutputBinding<T> result ) {
      result.setValue(message + " has been sent.");
      return message;
 }

Java 函数运行时库中,对其值将写入服务总线队列的函数参数使用 @QueueOutput 注释。 参数类型应为 OutputBinding<T>,其中 T 是 POJO 的任何本机 Java 类型。

Java 函数也可将内容写入服务总线主题。 以下示例使用 @ServiceBusTopicOutput 注释来说明输出绑定的配置。

@FunctionName("sbtopicsend")
    public HttpResponseMessage run(
            @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
            @ServiceBusTopicOutput(name = "message", topicName = "mytopicname", subscriptionName = "mysubscription", connection = "ServiceBusConnection") OutputBinding<String> message,
            final ExecutionContext context) {

        String name = request.getBody().orElse("Azure Functions");

        message.setValue(name);
        return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + name).build();

    }

以下示例显示计时器触发的 TypeScript 函数,该函数每 5 分钟发送一次队列消息。

import { app, InvocationContext, output, Timer } from '@azure/functions';

export async function timerTrigger1(myTimer: Timer, context: InvocationContext): Promise<string> {
    const timeStamp = new Date().toISOString();
    return `Message created at: ${timeStamp}`;
}

app.timer('timerTrigger1', {
    schedule: '0 */5 * * * *',
    return: output.serviceBusQueue({
        queueName: 'testqueue',
        connection: 'MyServiceBusConnection',
    }),
    handler: timerTrigger1,
});

若要输出多条消息,请返回一个数组而不是单个对象。 例如:

import { app, InvocationContext, output, Timer } from '@azure/functions';

export async function timerTrigger1(myTimer: Timer, context: InvocationContext): Promise<string[]> {
    // <displayInDocs>
    const timeStamp = new Date().toISOString();
    const message = `Message created at: ${timeStamp}`;
    return [`1: ${message}`, `2: ${message}`];
    // </displayInDocs>
}

app.timer('timerTrigger1', {
    schedule: '0 */5 * * * *',
    return: output.serviceBusQueue({
        queueName: 'testqueue',
        connection: 'MyServiceBusConnection',
    }),
    handler: timerTrigger1,
});

以下示例显示计时器触发的 JavaScript 函数,该函数每 5 分钟发送一次队列消息。

const { app, output } = require('@azure/functions');

const serviceBusOutput = output.serviceBusQueue({
    queueName: 'testqueue',
    connection: 'MyServiceBusConnection',
});

app.timer('timerTrigger1', {
    schedule: '0 */5 * * * *',
    return: serviceBusOutput,
    handler: (myTimer, context) => {
        const timeStamp = new Date().toISOString();
        return `Message created at: ${timeStamp}`;
    },
});

若要输出多条消息,请返回一个数组而不是单个对象。 例如:

const { app, output } = require('@azure/functions');

const serviceBusOutput = output.serviceBusQueue({
    queueName: 'testqueue',
    connection: 'MyServiceBusConnection',
});

app.timer('timerTrigger1', {
    schedule: '0 */5 * * * *',
    return: serviceBusOutput,
    handler: (myTimer, context) => {
        // <displayInDocs>
        const timeStamp = new Date().toISOString();
        const message = `Message created at: ${timeStamp}`;
        return [`1: ${message}`, `2: ${message}`];
        // </displayInDocs>
    },
});

以下示例演示了 function.json 文件中的服务总线输出绑定以及使用该绑定的 PowerShell 函数

下面是 function.json 文件中的绑定数据:

{
  "bindings": [
    {
      "type": "serviceBus",
      "direction": "out",
      "connection": "AzureServiceBusConnectionString",
      "name": "outputSbMsg",
      "queueName": "outqueue",
      "topicName": "outtopic"
    }
  ]
}

下面是创建消息作为函数输出的 PowerShell。

param($QueueItem, $TriggerMetadata) 

Push-OutputBinding -Name outputSbMsg -Value @{ 
    name = $QueueItem.name 
    employeeId = $QueueItem.employeeId 
    address = $QueueItem.address 
} 

以下示例演示如何写出到 Python 中的服务总线队列。 该示例取决于是使用 v1 还是 v2 Python 编程模型

import logging
import azure.functions as func

app = func.FunctionApp()

@app.route(route="put_message")
@app.service_bus_topic_output(arg_name="message",
                              connection="<CONNECTION_SETTING>",
                              topic_name="<TOPIC_NAME>")
def main(req: func.HttpRequest, message: func.Out[str]) -> func.HttpResponse:
    input_msg = req.params.get('message')
    message.set(input_msg)
    return 'OK'

特性

进程内独立工作进程 C# 库都使用属性来定义输出绑定。 C# 脚本改用 function.json 配置文件,如 C# 脚本指南中所述。

C# 类库中,使用 ServiceBusOutputAttribute 定义由输出写入的队列或主题。

下表说明了可使用特性设置的属性:

属性 说明
EntityType 将实体类型设置为 Queue(用于将消息发送到队列)或 Topic(将消息发送到主题)。
QueueOrTopicName 要向其发送消息的主题或队列的名称。 使用 EntityType 设置目标类型。
Connection 指定如何连接到服务总线的应用设置或设置集合的名称。 请参阅连接

修饰符

仅适用于 Python v2 编程模型。

对于使用修饰器定义的 Python v2 功能,支持 service_bus_topic_output 上的以下属性:

properties 说明
arg_name 变量的名称,表示函数代码中的队列或主题消息。
queue_name 队列名称。 仅在发送队列消息的情况下设置,不为主题设置。
topic_name 主题名称。 仅在发送主题消息的情况下设置,不为队列设置。
connection 指定如何连接到服务总线的应用设置或设置集合的名称。 请参阅连接

对于使用 function.json 定义的 Python 函数,请参阅“配置”部分。

批注

使用 ServiceBusQueueOutputServiceBusTopicOutput 注释可以函数输出的形式写入消息。 使用这些注释修饰的参数必须声明为 OutputBinding<T>,其中 T 是与消息类型对应的类型。

在本地开发时,需要将应用程序设置添加到 Values 集合中的 local.settings.json 文件中。

配置

仅适用于 Python v1 编程模型

下表说明了可以在传递给 output.serviceBusQueue() 方法的 options 对象上设置的属性。

properties 说明
queueName 队列名称。
连接 指定如何连接到服务总线的应用设置或设置集合的名称。 请参阅连接

下表说明了可以在传递给 output.serviceBusTopic() 方法的 options 对象上设置的属性。

properties 说明
topicName 主题名称。
连接 指定如何连接到服务总线的应用设置或设置集合的名称。 请参阅连接

在本地开发时,需要将应用程序设置添加到 Values 集合中的 local.settings.json 文件中。

下表解释了在 function.json 文件和 ServiceBus 特性中设置的绑定配置属性。

function.json 属性 说明
type 必须设置为“serviceBus”。 在 Azure 门户中创建触发器时,会自动设置此属性。
direction 必须设置为“out”。 在 Azure 门户中创建触发器时,会自动设置此属性。
name 变量的名称,表示函数代码中的队列或主题消息。 设置为“$return”可引用函数返回值。
queueName 队列名称。 仅在发送队列消息的情况下设置,不为主题设置。
topicName 主题名称。 仅在发送主题消息的情况下设置,不为队列设置。
连接 指定如何连接到服务总线的应用设置或设置集合的名称。 请参阅连接
accessRights(仅限 v1) 连接字符串的访问权限。 可用值为 managelisten。 默认值是 manage,其指示 connection 具有“管理”权限。 如果使用不具有“管理”权限的连接字符串,请将 设置为“listen”。 否则,Functions 运行时可能会在尝试执行需要管理权限的操作时失败。 在 Azure Functions 版本 2.x 及更高版本中,此属性不可用,因为最新版本的服务总线 SDK 不支持管理操作。

在本地开发时,请将应用程序设置添加到 Values 集合的 local.settings.json 文件

有关完整示例,请参阅示例部分

使用情况

所有 C# 形式和扩展版本都支持以下输出参数类型:

类型 说明
System.String 如果要写入的消息是简单的文本,请使用此类型。 如果函数退出时参数值为 null,Functions 不创建消息。
byte[] 如果要写入二进制数据消息,请使用此类型。 如果函数退出时参数值为 null,Functions 不创建消息。
Object 如果消息包含 JSON,Functions 会将对象序列化为 JSON 消息负载。 如果函数退出时参数值为 null,Functions 将创建具有 null 对象的消息。

特定于消息的参数类型包含其他消息元数据。 输出绑定支持的特定类型取决于 Functions 运行时版本、扩展包版本和所使用的 C# 形式。

如果希望函数写入单个消息,服务总线输出绑定可以绑定到以下类型:

类型 说明
string 字符串格式的消息。 当消息为简单文本时使用。
byte[] 消息的字节数。
JSON 可序列化类型 表示消息的对象。 函数尝试将普通旧 CLR 对象 (POCO) 类型序列化为 JSON 数据。

如果希望函数写入多个消息,服务总线输出绑定可以绑定到以下类型:

类型 说明
T[],其中 T 是单消息类型之一 包含多个消息的数组。 每个条目表示一个消息。

对于其他输出方案,请直接创建和使用 Azure.Messaging.ServiceBus 中的类型。

在 Azure Functions 1.x 中,如果队列尚不存在并且 accessRights 设置为 manage,则运行时会创建队列。 在 Azure Functions 版本 2.x 及更高版本中,队列或主题必须已存在;如果指定了不存在的队列或主题,则函数会失败。

使用 Azure 服务总线 SDK 而不是内置的输出绑定。

通过直接返回值或使用 context.extraOutputs.set() 来访问输出消息。

通过 Push-OutputBinding cmdlet 可以输出到服务总线,你在其中传递的参数与 function.json 文件中绑定的名称参数指定的名称匹配。

使用 Azure 服务总线 SDK 而不是内置的输出绑定。

有关完整示例,请参阅示例部分

连接

connection 属性是对环境配置的引用,它指定应用应该如何连接到服务总线。 它可能指定:

如果配置的值既是单个设置的完全匹配,也是其他设置的前缀匹配,则使用完全匹配。

连接字符串

若要获取连接字符串,请执行获取管理凭据中显示的步骤。 必须是服务总线命名空间的连接字符串,不限于特定的队列或主题。

此连接字符串应存储在应用程序设置中,其名称与绑定配置的 connection 属性指定的值匹配。

如果应用设置名称以“AzureWebJobs”开头,则只能指定该名称的余下部分。 例如,如果将 connection 设为“MyServiceBus”,Functions 运行时会查找名为“AzureWebJobsMyServiceBus”的应用设置。 如果将 connection 留空,函数运行时将使用名为“AzureWebJobsServiceBus”的应用设置中的默认服务总线连接字符串。

基于标识的连接

如果使用 5.x 版或更高版本的扩展,则无需将连接字符串与机密一起使用,而是可以使应用使用 Azure Active Directory 标识。 为此,需要定义公共前缀下的设置,该前缀映射到触发器和绑定配置中的 connection 属性。

在此模式下,扩展需要以下属性:

属性 环境变量模板 说明 示例值
完全限定的命名空间 <CONNECTION_NAME_PREFIX>__fullyQualifiedNamespace 完全限定的服务总线命名空间。 <service_bus_namespace>.servicebus.chinacloudapi.cn

可以设置其他属性来自定义连接。 请参阅基于标识的连接的通用属性

注意

使用 Azure 应用程序配置Key Vault 为托管标识连接提供设置时,设置名称应使用有效的键分隔符(例如 :/)替代 __,以确保正确解析名称。

例如 <CONNECTION_NAME_PREFIX>:fullyQualifiedNamespace

在 Azure Functions 服务中托管时,基于标识的连接将使用托管标识。 默认情况下使用系统分配的标识,但可以使用 credentialclientID 属性来指定用户分配的标识。 请注意,不支持为用户分配的标识配置资源 ID。 在其他上下文(如本地开发)中运行时,将改用开发人员标识,尽管可以进行自定义。 请参阅使用基于标识的连接进行本地开发

向标识授予权限

无论使用何种标识,都必须具有执行所需操作的权限。 需要使用内置角色或者提供这些权限的自定义角色在 Azure RBAC 中分配角色

重要

某些权限可能由并非所有上下文都需要的目标服务公开。 尽可能遵循最低权限原则,仅授予标识所需的权限。 例如,如果应用只需要从数据源进行读取即可,则使用仅具有读取权限的角色。 分配一个也具有该服务写入权限的角色并不恰当,因为对于读取操作来说,写入是多余的权限。 同样,你也希望确保角色分配的范围仅限于需要读取的资源。

你将需要创建一个角色分配,以便在运行时提供对主题和队列的访问权限。 所有者等管理角色还不够。 下表显示了在正常操作中使用服务总线扩展时建议使用的内置角色。 根据所编写的代码,应用程序可能需要具有其他权限。

绑定类型 内置角色示例
触发器1 Azure 服务总线数据接收方Azure 服务总线数据所有者
输出绑定 Azure 服务总线数据发送方

1 若要从服务总线主题触发,角色分配需要对服务总线订阅资源具有有效范围。 如果仅包含主题,将发生错误。 某些客户端(例如 Azure 门户)不会将服务总线订阅资源公开为角色分配的范围。 在这种情况下,可以改用 Azure CLI。 若要了解详细信息,请参阅适用于 Azure 服务总线的 Azure 内置角色

异常和返回代码

绑定 参考
服务总线 服务总线错误代码
服务总线 服务总线限制

后续步骤