次の方法で共有


Azure Functions 開発者ガイド

Azure Functions では、好みの言語や開発環境に関係なく、すべての関数がいくつかの主要な技術的概念とコンポーネントを共有します。 この記事は言語固有です。 記事の上部で好みの言語を選んでください。

この記事では、「Azure Functions の概要」を既に読んでいることを前提としています。

すぐに始める場合は、Visual StudioVisual Studio Code、またはコマンド プロンプトからクイックスタート チュートリアルを完了できます。

すぐに始める場合は、Maven (コマンド ライン)、EclipseIntelliJ IDEAGradleQuarkusSpring Cloud、または Visual Studio Code を使ってクイックスタート チュートリアルを完了できます。

すぐに始める場合は、Visual Studio Code を使って、またはコマンド プロンプトからクイックスタート チュートリアルを完了できます。

すぐに始める場合は、Visual Studio Code を使って、またはコマンド プロンプトからクイックスタート チュートリアルを完了できます。

すぐに始める場合は、Visual Studio Code を使って、またはコマンド プロンプトからクイックスタート チュートリアルを完了できます。

すぐに始める場合は、Visual Studio Code を使って、またはコマンド プロンプトからクイックスタート チュートリアルを完了できます。

コード プロジェクト

Azure Functions の中核となるのは、"関数" という 1 つ以上のコード実行単位を実装する言語固有のコード プロジェクトです。 関数は、イベントに基づいて、HTTP 要求に対する応答として、またはスケジュールに基づいて、Azure クラウドで実行される単なるメソッドです。 Azure Functions コード プロジェクトは、Azure での実行時に、プロジェクト内の個々の関数を整理し、デプロイし、まとめて管理するためのメカニズムだと考えてください。 詳細については、「関数を整理する」を参照してください。

コード プロジェクトをレイアウトする方法と、プロジェクトのどのメソッドが関数であるかを示す方法は、プロジェクトの開発言語によって異なります。 言語固有の詳細なガイダンスについては、C# 開発者ガイドを参照してください。

コード プロジェクトをレイアウトする方法と、プロジェクトのどのメソッドが関数であるかを示す方法は、プロジェクトの開発言語によって異なります。 言語固有のガイダンスについては、Java 開発者ガイドを参照してください。

コード プロジェクトをレイアウトする方法と、プロジェクトのどのメソッドが関数であるかを示す方法は、プロジェクトの開発言語によって異なります。 言語固有のガイダンスについては、Node.js 開発者ガイドを参照してください。

コード プロジェクトをレイアウトする方法と、プロジェクトのどのメソッドが関数であるかを示す方法は、プロジェクトの開発言語によって異なります。 言語固有のガイダンスについては、PowerShell 開発者ガイドを参照してください。

コード プロジェクトをレイアウトする方法と、プロジェクトのどのメソッドが関数であるかを示す方法は、プロジェクトの開発言語によって異なります。 言語固有のガイダンスについては、Python 開発者ガイドを参照してください。

すべての関数には、関数の開始方法を定義し、関数に入力を提供できるトリガーが必要です。 関数には、必要に応じて、入力バインディングと出力バインディングを定義できます。 これらのバインディングにより、クライアント SDK を使わなくても、他のサービスへの接続が簡単になります。 詳細については、「Azure Functions でのトリガーとバインドの概念」を参照してください。

Azure Functions には、一連の言語固有のプロジェクトと関数のテンプレートが用意されています。これを利用することで、新しいコード プロジェクトを作成し、プロジェクトに関数を追加することが簡単になります。 Azure Functions 開発をサポートする任意のツールを使い、これらのテンプレートを使って新しいアプリと関数を生成できます。

開発ツール

以下のツールでは、好みの言語で Azure Functions の統合された開発と発行のエクスペリエンスを利用できます。

これらのツールは Azure Functions Core Tools と統合されているため、Functions ランタイムを使ってローカル コンピューター上で実行およびデバッグできます。 詳細については、「Azure Functions をローカルでコーディングしてテストする」を参照してください。

Azure portal にはエディターもあり、コードと function.json 定義ファイルはポータルで直接更新できます。 このエディターは、軽微な変更や概念実証関数の作成にのみ使ってください。 可能であれば、常に関数をローカルで開発するようにします。 詳細については、「Azure Portal で初めての関数を作成する」を参照してください。

ポータル編集は、function.json ファイルを使う Node.js バージョン 3 でのみサポートされています。

展開

コード プロジェクトを Azure に発行するときは、基本的にプロジェクトを既存の関数アプリ リソースにデプロイします。 関数アプリからは、関数が実行される、Azure における実行コンテキストが提供されます。 そのため、これが関数のデプロイと管理の単位となります。 Azure Resource の観点から見ると、関数アプリは Azure App Service のサイト リソース (Microsoft.Web/sites) に相当し、これは Web アプリに相当します。

関数アプリは、まとめて管理、デプロイ、およびスケールされる 1 つまたは複数の個々の関数で構成されます。 関数アプリ内のすべての関数は、同じ料金プランデプロイ方法ランタイム バージョンを共有します。 詳細については、関数アプリの管理方法に関する記事を参照してください。

関数アプリとその他の必要なリソースがまだ Azure に存在しない場合は、プロジェクト ファイルをデプロイする前に、まずこれらのリソースを作成する必要があります。 これらのリソースは、次のいずれかの方法で作成できます。

Functions は、ツールベースの発行に加えて、既存の関数アプリにソース コードをデプロイするための他のテクノロジもサポートしています。 詳細については、「Azure Functions のデプロイ テクノロジ」を参照してください。

サービスへの接続

クラウドベースのコンピューティング サービスの主要な要件は、他のクラウド サービスとの間でデータの読み取りとデータの書き込みを行うことです。 Functions にはさまざまなバインディングが用意されており、クライアント SDK を使わなくても簡単にサービスに接続できます。

Functions に用意されているバインディング拡張機能を使う場合でも、クライアント SDK を直接使う場合でも、接続データはセキュリティで保護された方法で格納し、コードには含めないでください。 詳細については、「接続」を参照してください。

バインド

Functions には、多くの Azure サービスと、拡張機能として実装されているいくつかのサードパーティ サービス用にバインディングが用意されています。 詳細については、サポートされているバインディングの詳細な一覧を参照してください。

バインディング拡張機能は、入力と出力の両方をサポートしており、多くのトリガーも入力バインディングとして機能します。 バインディングを使うと、Functions ホストがデータ アクセスを自動処理できるようにサービスへの接続を構成できます。 詳細については、「Azure Functions でのトリガーとバインドの概念」を参照してください。

バインディングに由来するエラーに関する問題がある場合は、Azure Functions のバインディング エラー コードのドキュメントを参照してください。

クライアント SDK

Functions には関数コードでのデータ アクセスを簡単にするバインディングが用意されていますが、必要に応じて、プロジェクトでクライアント SDK を使って特定のサービスに直接アクセスすることもできます。 バインディング拡張機能でサポートされていない、基となる SDK の機能が関数に必要な場合は、必要に応じてクライアント SDK を直接使います。

クライアント SDK を使う場合、バインディング拡張機能で使う接続文字列の格納とアクセスに同じプロセスを使う必要があります。

関数内でクライアント SDK インスタンスを作成するときは、環境変数からクライアントに必要な接続情報を取得する必要があります。

関数内でクライアント SDK インスタンスを作成するときは、環境変数からクライアントに必要な接続情報を取得する必要があります。

関数内でクライアント SDK インスタンスを作成するときは、環境変数からクライアントに必要な接続情報を取得する必要があります。

関数内でクライアント SDK インスタンスを作成するときは、環境変数からクライアントに必要な接続情報を取得する必要があります。

関数内でクライアント SDK インスタンスを作成するときは、環境変数からクライアントに必要な接続情報を取得する必要があります。

つながり

セキュリティのベスト プラクティスとして、Azure Functions は Azure App Service のアプリケーション設定機能を利用し、他のサービスへの接続に必要な文字列やキーなどのトークンを、より安全な方法で格納できるようにします。 Azure のアプリケーション設定は暗号化されて格納され、実行時にアプリから環境変数 name value ペアとしてアクセスできます。 接続プロパティを必要とするトリガーとバインディングの場合は、実際の接続文字列ではなくアプリケーション設定名を設定します。 接続文字列またはキーを使ってバインドを直接構成することはできません。

たとえば、connection プロパティを含むトリガー定義があるとします。 接続文字列ではなく、connection を、接続文字列を含む環境変数の名前に設定します。 このシークレット アクセス戦略を使うと、アプリの安全性を高め、環境間で接続を簡単に変更できます。 セキュリティをさらに強化するには、ID ベースの接続を使用できます。

既定の構成プロバイダーでは環境変数を使用します。 これらの変数は、Azure で実行する場合はアプリケーション設定で、ローカルで開発する場合はローカル設定ファイルで定義します。

接続値

接続名が 1 つの正確な値に解決されると、ランタイムでは、値を接続文字列として識別します。これには通常、シークレットが含まれます。 接続文字列の詳細は、接続先のサービスによって異なります。

ただし、接続名は、複数の構成アイテムのコレクションを参照することもできます。これは、ID ベースの接続を構成する場合に役立ちます。 2 つのアンダースコア __ で終わる共有プレフィックスを使用して、環境変数をコレクションとして扱うことができます。 このプレフィックスに接続名を設定することによって、グループを参照できます。

たとえば、Azure Blob トリガー定義の connection プロパティが Storage1 であるとします。 Storage1 という名前の環境変数で構成された単一の文字列値がない限り、Storage1__blobServiceUri という名前の環境変数を使用して、接続の blobServiceUri プロパティを通知できます。 接続のプロパティはサービスによって異なります。 接続を使用するコンポーネントのドキュメントを参照してください。

注意

Azure App Configuration または Key Vault を使用してマネージド ID 接続の設定を指定する場合、__ の代わりに :/ などの有効なキー区切り記号を使用して、名前が正しく解決されるようにしなければなりません。

たとえば、「 Storage1:blobServiceUri 」のように入力します。

ID ベースの接続を構成する

Azure Functions の一部の接続は、シークレットの代わりに ID を使用するように構成できます。 サポートは、接続を使用する拡張機能によって異なります。 場合によっては、接続先のサービスで ID ベースの接続がサポートされている場合でも、Functions で接続文字列が必要になることがあります。 マネージド ID を使用して関数アプリを構成するチュートリアルについては、ID ベースの接続を使用した関数アプリの作成に関 するチュートリアルを参照してください

Note

従量課金プランまたは Elastic Premium プランで実行するとき、アプリは、関数アプリで使用されるストレージ アカウントの Azure Files に接続するときに WEBSITE_AZUREFILESCONNECTIONSTRINGWEBSITE_CONTENTSHARE の設定を使用します。 Azure Files では、ファイル共有にアクセスするときにマネージド ID を使用することはできません。 詳細については、「Azure Files でサポートされている認証シナリオ」を参照してください

次のコンポーネントは、ID ベースの接続をサポートしています。

接続元 サポートされているプラン 詳細情報
Azure BLOB のトリガーとバインディング すべて Azure BLOB 拡張機能バージョン 5.0.0 以降
拡張機能バンドル 3.3.0 以降
Azure Queues のトリガーとバインディング すべて Azure Queues 拡張機能バージョン 5.0.0 以降
拡張機能バンドル 3.3.0 以降
Azure テーブル (Azure Storage を使用している場合) すべて Azure Tables 拡張機能バージョン 1.0.0 以降
拡張機能バンドル 3.3.0 以降
Azure SQL データベース すべて マネージ ID と SQL バインドを使用して Azure SQL に関数アプリを接続する
Azure Event Hubs のトリガーとバインディング All Azure Event Hubs 拡張機能バージョン 5.0.0 以降
拡張機能バンドル 3.3.0 以降
Azure Service Bus のトリガーとバインディング All Azure Service Bus 拡張機能バージョン 5.0.0 以降
拡張機能バンドル 3.3.0 以降
Azure Event Grid 出力バインド すべて Azure Event Grid 拡張機能バージョン 3.3.0 以降
拡張機能バンドル 3.3.0 以降
Azure Cosmos DB のトリガーとバインディング すべて Azure Cosmos DB 拡張機能バージョン 4.0.0 以降
拡張機能バンドル 4.0.2 以降
Azure SignalR のトリガーとバインド All Azure SignalR 拡張機能バージョン 1.7.0 以降
拡張機能バンドル 3.6.1 以降
Durable Functions ストレージ プロバイダー (Azure Storage) All Durable Functions 拡張機能バージョン 2.7.0 以降
拡張機能バンドル 3.3.0 以降
ホスト (必須) ストレージ ("AzureWebJobsStorage") All ID を使用してホスト ストレージに接続する

Azure Functions サービスでホストされている場合、ID ベースの接続では、マネージド ID が使用されます。 ユーザー割り当て ID を credential および clientID プロパティで指定できますが、システム割り当て ID が既定で使用されます。 リソース ID を使用したユーザー割り当て ID の構成はサポートされていないことに注意してください。 ローカル開発などの他のコンテキストで実行する場合は、代わりに開発者 ID が使用されますが、カスタマイズすることもできます。 ID ベースの接続によるローカル開発に関するページをご覧ください。

ID にアクセス許可を付与する

使用されている ID が何であれ、目的のアクションを実行するためのアクセス許可が必要です。 ほとんどの Azure では、これはそれらのアクセス許可を提供する組み込みロールまたはカスタム ロールを使って、Azure RBAC でロールを割り当てる必要があることを意味します。

重要

すべてのコンテキストに必要ではない一部のアクセス許可がターゲット サービスによって公開される場合があります。 可能であれば、最小限の特権の原則に従い、必要な特権だけを ID に付与します。 たとえば、アプリがデータ ソースからの読み取りのみを行う必要がある場合は、読み取りアクセス許可のみを持つロールを使用します。 サービスへの書き込みも可能なロールを割り当てることは、読み取り操作に対するアクセス許可が過剰になるため、不適切です。 同様に、ロールの割り当てが、読み取る必要のあるリソースだけに限定されていることを確認する必要があります。

各コンポーネントのアクセス許可については、次のいずれかのタブを選んでください。

実行時に BLOB コンテナーへのアクセスを提供するロールの割り当てを作成する必要があります。 所有者のような管理ロールでは十分ではありません。 次の表は、通常の操作で Blob Storage の拡張機能を使用するときに推奨される組み込みロールを示しています。 アプリケーションでは、記述したコードに基づいて追加のアクセス許可が必要になる場合があります。

[バインドの種類] 組み込みロールの例
トリガー ストレージ BLOB データ所有者およびストレージ キュー データ共同作成者1

AzureWebJobsStorage 接続にも追加のアクセス許可を付与する必要があります。2
入力バインド ストレージ BLOB データ閲覧者
出力バインド ストレージ BLOB データ所有者

1 BLOB トリガーは、複数回にわたる再試行の失敗を、接続によって指定されたストレージ アカウント上のキューに有害な BLOB を書き込むことにより処理します。

2 AzureWebJobsStorage 接続は、トリガーを有効にする BLOB やキューのために内部的に使用されます。 ID ベースの接続を使用するように構成されている場合は、既定の要件を超える追加のアクセス許可が必要になります。 必要なアクセス許可は、ストレージ BLOB データ所有者ストレージ キュー データ共同作成者、およびストレージ アカウント共同作成者の各ロールによって満たされます。 詳細については、「ID を使用してホスト ストレージに接続する」を参照してください。

ID ベース接続に共通のプロパティ

Azure サービスの ID ベース接続では、次の共通プロパティが受け入れられます。ここで <CONNECTION_NAME_PREFIX> は、トリガーまたはバインディング定義内の connection プロパティの値です。

プロパティ 環境変数テンプレート 説明
トークン資格情報 <CONNECTION_NAME_PREFIX>__credential 接続のためにトークンを取得する方法を定義します。 デプロイされた Azure Function でマネージド ID 認証を使用する場合は、この設定を managedidentity に設定する必要があります。 この値は、ホスティング環境でマネージド ID が使用できる場合にのみ有効です。
クライアント ID <CONNECTION_NAME_PREFIX>__clientId credentialmanagedidentity に設定されると、このプロパティによって、トークン取得時に使用されるユーザー割り当て ID を指定できます。 このプロパティは、アプリケーションに割り当てられたユーザー割り当て ID に相当するクライアント ID を受け取ります。 リソース ID とクライアント ID の両方を指定することは無効です。 指定されていない場合、システム割り当て ID が使用されます。 このプロパティは、credential を設定しないとき、ローカル開発シナリオで異なる方法で使用されます。
Resource ID <CONNECTION_NAME_PREFIX>__managedIdentityResourceId credentialmanagedidentity に設定されると、このプロパティによって、トークン取得時に使用されるリソース ID を指定できます。 プロパティは、ユーザー定義マネージド ID のリソース ID に対応するリソース識別子を受け取ります。 リソース ID とクライアント ID の両方を指定することは無効です。 両方とも指定されていない場合は、システム割り当て ID が使用されます。 このプロパティは、credential を設定しないとき、ローカル開発シナリオで異なる方法で使用されます。

特定の接続の種類に対して、他のオプションがサポートされている場合があります。 接続を確立するコンポーネントのドキュメントを参照してください。

ID ベースの接続によるローカル開発

Note

ID ベースの接続によるローカル開発には、Azure Functions Core Tools のバージョン 4.0.3904 以降のバージョンが必要です。

関数プロジェクトをローカルで実行している場合、上記の構成によって、ローカルの開発者 ID を使用するようにランタイムに指示します。 接続では次の場所から順番にトークンを取得しようとします。

  • Microsoft アプリケーション間で共有されるローカル キャッシュ
  • Visual Studio の現在のユーザー コンテキスト
  • Visual Studio Code の現在のユーザー コンテキスト
  • Azure CLI の現在のユーザー コンテキスト

これらのオプションのいずれも成功しなかった場合は、エラーが発生します。

ID には、開発に使用される Azure リソースに対して既にいくつかのロール割り当てが存在する場合がありますが、これらのロールでは必要なデータ アクセスが提供されない可能性があります。 所有者のような管理ロールでは十分ではありません。 各コンポーネントの接続に必要なアクセス許可を再確認し、それらが自分に割り当てられていることを確認してください。

場合によっては、別の ID の使用を指定したい場合があります。 Microsoft Entra サービス プリンシパルのクライアント ID とクライアント シークレットに基づいて、代替 ID をポイントする接続の構成プロパティを追加できます。 Azure Functions サービスでホストされている場合、この構成オプションはサポートされません。 ローカル コンピューターで ID とシークレットを使用するには、次の追加のプロパティを指定して接続を定義します。

プロパティ 環境変数テンプレート 説明
テナント ID <CONNECTION_NAME_PREFIX>__tenantId Microsoft Entra テナント (ディレクトリ) ID。
Client ID <CONNECTION_NAME_PREFIX>__clientId テナント内のアプリの登録のクライアント (アプリケーション) ID。
クライアント シークレット <CONNECTION_NAME_PREFIX>__clientSecret アプリの登録で生成されたクライアント シークレット。

Azure BLOB への ID ベース接続に必要な local.settings.json プロパティの例を以下に示します。

{
  "IsEncrypted": false,
  "Values": {
    "<CONNECTION_NAME_PREFIX>__blobServiceUri": "<blobServiceUri>",
    "<CONNECTION_NAME_PREFIX>__queueServiceUri": "<queueServiceUri>",
    "<CONNECTION_NAME_PREFIX>__tenantId": "<tenantId>",
    "<CONNECTION_NAME_PREFIX>__clientId": "<clientId>",
    "<CONNECTION_NAME_PREFIX>__clientSecret": "<clientSecret>"
  }
}

ID を使用してホスト ストレージに接続する

Azure Functions ホストでは、AzureWebJobsStorage で設定されたストレージ接続を使用することで、タイマー トリガーのシングルトン実行の調整や既定のアプリ キーの格納などのコア動作が可能になります。 この接続は、ID を使うように構成することもできます。

注意事項

Functions の他のコンポーネントは、既定の動作では AzureWebJobsStorage に依存します。 ID ベースという種類の接続をサポートしていない以前のバージョンの拡張機能 (Azure BLOB、Event Hubs、Durable Functions のトリガーやバインディングを含む) を使用している場合は、これを ID ベースの接続に移行しないでください。 同様に、Linux 従量課金プランでサーバー側のビルドを使用した場合は、AzureWebJobsStorage がデプロイ成果物に使用されます。また、これを有効にする場合は、外部のデプロイ パッケージを使用してデプロイする必要があります。

また、関数アプリでは、トリガー、バインディング、関数コードで、別のストレージ接続に AzureWebJobsStorage を再利用している場合があります。 接続文字列からこの接続を変更する前に、AzureWebJobsStorage のすべての用途で ID ベースの接続形式を使用できるようにします。

AzureWebJobsStorage で ID ベースの接続を使用するには、次のアプリ設定を構成します。

設定 説明 値の例
AzureWebJobsStorage__blobServiceUri HTTPS スキームを使用する、ストレージ アカウントの BLOB サービスのデータ プレーン URI。 https://<storage_account_name>.blob.core.windows.net
AzureWebJobsStorage__queueServiceUri HTTPS スキームを使用する、ストレージ アカウントのキュー サービスのデータ プレーン URI。 https://<storage_account_name>.queue.core.windows.net
AzureWebJobsStorage__tableServiceUri HTTPS スキームを使用する、ストレージ アカウントのテーブル サービスのデータ プレーン URI。 https://<storage_account_name>.table.core.windows.net

同様に ID ベース接続に共通のプロパティを設定することもできます。

グローバル Azure の既定の DNS サフィックスとサービス名を使用するストレージ アカウントを使って AzureWebJobsStorage を構成している場合は、https://<accountName>.[blob|queue|file|table].core.windows.net 形式に従って、代わりにストレージ アカウントの名前に AzureWebJobsStorage__accountName を設定できます。 各ストレージ サービスのエンドポイントは、このアカウントに対して推論されます。 ストレージ アカウントがソブリン クラウドにある場合、またはカスタム DNS がある場合、これは機能しません。

設定 説明 値の例
AzureWebJobsStorage__accountName ストレージ アカウントのアカウント名。アカウントがソブリン クラウドに存在せず、カスタム DNS が与えられていない場合にのみ有効。 この構文は AzureWebJobsStorage に固有であり、他の ID ベースの接続には使用できません。 <storage_account_name>

実行時に "AzureWebJobsStorage" のストレージ アカウントにアクセスできるようにするロールの割り当てを作成する必要があります。 所有者のような管理ロールでは十分ではありません。 ストレージ BLOB データ所有者ロールでは、Functions ホスト ストレージの基本ニーズが対処されます。ランタイムでは、BLOB の読み取りと書き込みの両方が必要であり、また、コンテナーを作成できる必要があります。 いくつかの拡張機能では、この接続を BLOB、キュー、テーブルの既定の場所として使用するため、これらの使用により、次の表に示されている要件が追加される可能性があります。 他の目的で "AzureWebJobsStorage" を使用する場合は、追加のアクセス許可が必要な場合があります。

拡張機能 必要なロール 説明
拡張機能なし (ホストのみ) ストレージ BLOB データ所有者 一般的な調整、既定のキー ストアに使用されます。
Azure BLOB (トリガーのみ) すべてを満たす:
ストレージ アカウント共同作成者
ストレージ BLOB データ所有者
ストレージ キュー データ共同作成者共同作成者
BLOB トリガーは内部的に Azure キューを使用し、BLOB の配信確認メッセージを書き込みます。 トリガー用に構成されている接続には関係なく、これらに AzureWebJobsStorage を使用します。
Azure Event Hubs (トリガーのみ) (既定の要件からの変更はなし)
ストレージ BLOB データ所有者
チェックポイントは、AzureWebJobsStorage 接続を使用して BLOB 内に保持されます。
タイマー トリガー (既定の要件からの変更はなし)
ストレージ BLOB データ所有者
イベントあたり 1 回の実行を保証するために、AzureWebJobsStorage 接続を使用して BLOB に対してロックが取得されます。
Durable Functions すべてを満たす:
ストレージ BLOB データ共同作成者
ストレージ キュー データ共同作成者
ストレージ テーブル データ共同作成者
Durable Functions は BLOB、キュー、テーブルを使用してアクティビティ関数を調整し、オーケストレーションの状態を維持します。 既定では、これらのすべてに AzureWebJobsStorage 接続を使用しますが、Durable Functions 拡張機能の構成で別の接続を指定できます。

問題の報告

Item 説明 Link
ランタイム スクリプト ホスト、トリガー、バインド、言語のサポート 問題のファイリング
テンプレート 作成テンプレートに関するコードの問題 問題のファイリング
ポータル ユーザー インターフェイスまたはエクスペリエンスの問題 問題のファイリング

オープン ソース リポジトリ

Azure Functions のコードはオープン ソースであり、次の GitHub リポジトリで主要なコンポーネントを見つけることができます。

次のステップ

詳細については、次のリソースを参照してください。