最近 Microsoft の公式リポジトリに Microsoft/vs4mac-labsなるものが追加されました。
下記のようなカテゴリ別にチュートリアルが用意されていて、それぞれ思った以上に詳細でいい感じです。
- Azure Functions
- Docker
- Internet of Things
- Mobile
- Unity
- Web
ただ、英語しかなかったので、たまたま VS for Mac で Azure Functions を開発したい気分だったので Azure Functions 分だけ訳してみることにしました。
現在最新版の Mono にバグがあるようでスクショは英語版のものをそのまま借用していますがハンズオンには差し障りなさそうです。
本家リポジトリに送ったPRは無事mergeされました。日本語版のVS for Macのスクショで追いたい方はそちらを参照してみてください。
Visual Studio for Mac で Azure Functions を作成する
概要
Azure Functions は開発を加速するための、クラウドにおけるイベントベースのサーバレスコンピューティング体験です。アプリケーション全体やそれを実行するインフラを心配することなく、問題に必要なコードだけ集中することができます。Functions は開発をより生産的にすることができ、C#、F#、Node.js、Python、PHP などの開発言語を使用することができます。費用が発生するのはコードが実行されている時間だけで、必要に応じてスケーリングを Azure に任せることができます。Azure Functions を使用すると、Microsoft Azure でサーバーレスアプリケーションを開発できます。
このラボでは、Visual Studio for Mac を使用して Azure Functions を作成する方法を学習します。また、Azure Functions の開発者が利用できる多くの種類のバインディングとトリガーの1つである Azure ストレージテーブルと統合します。
目標
- ローカル Azure Functions の作成とデバッグ
- Web と Azure ストレージリソースとの統合
- 複数の Azure Functions を含むワークフローの編成
前提条件
- Azure Functions 開発拡張機能をインストールした Visual Studio for Mac(https://www.visualstudio.com/vs/visual-studio-mac)
- Azure Functions サポートはプレビューとして利用可能なため、現在このラボは Visual Studio for Mac をアルファチャネルに切り替える必要があることにご注意ください。
- Azure Functions の開発拡張機能をインストールするためには、トップメニューの 拡張機能... をクリックして、ギャラリー タブの IDE Extensions ノードの Azure Functions development(preview) 拡張を見つけます。
- Azure サブスクリプション(https://azure.microsoft.com/ja-jp/free/ で無料利用可能です)
対象者
このラボは、C# に精通した開発者を対象としていますが、深い経験は必要ありません。
エクササイズ1: Visual Studio for Mac で Azure Functions を作成する
タスク1: Azure Functions プロジェクトの作成
1. Visual Studio for Mac を起動してください。
2. ファイル > 新しいソリューション...を選択してください。
3. クラウド > 全般 カテゴリーから Azure Functions テンプレートを選択してください。Azure Functions をホストする .NET クラスライブラリを作成するには C# を使用します。次へ をクリックしてください。
4. プロジェクト名 を "AzureFunctionsLab" に設定し、作成をクリックします。
5. ソリューションエクスプローラでノードを展開してください。デフォルトのプロジェクトテンプレートには、さまざまな Azure WebJob パッケージや Newtonsoft.Json パッケージへの NuGet 参照が含まれています。デフォルトで2つの空の接続文字列設定があります。
タスク2: Azure ストレージアカウントの作成
1. ブラウザを開き、https://portal.azure.com で自分のアカウントにログインします。
2. 新規をクリックし、storageを検索して enter をクリックします。
3. ストレージアカウント - blob, file, table, queueをクリックして新しい Azure ストレージアカウントを作成してください。
4. 作成をクリックしてください。
5. 名前にグローバルに一意な名前を入力し、リソースグループ に再利用します。"azurefunctionshoge" のような名前を使用できます。作成 をクリックしてください。
6. アカウントの作成には1分ほどかかります。成功通知をクリックしてダッシュボードを開いてください。
7. Access keysタブを選択してください。
8. 1つ目の 接続文字列 をコピーしてください。これは後ほど Azure ストレージと Azure Functions を統合するために使用します。
9. Visual Studio for Mac に戻り、local.settings.json に AzureWebJobsStorage の設定としてコネクションストリングをペーストしてください。これで、リソースにアクセスする必要のある Functions の属性の設定名を簡単に参照できます。
タスク3: Azure Function の作成とデバッグ
1. これでコードを追加する準備が整いました。 .NET クラスライブラリで作業する場合、Azure Functions は静的メソッドとして追加されます。ソリューションエクスプローラで AzureFunctionsLab プロジェクトノードを右クリックし、 追加 > 新しいファイル... を選択してください。
2. General カテゴリーから 空のクラス テンプレートを選択してください。名前 を "Functions" に設定し、新規(N) をクリックしてください。
3. 新しいファイルの先頭に、以下の using ステートメントを追加します。これらは、 LINQ や HTTP などの機能や、Azure Functions スタックのオブジェクトを扱うためのさまざまなヘルパーメソッドへのアクセスを提供します。
using System.Linq;
using System.Net;
using System.Net.Http;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.WindowsAzure.Storage.Table;
4. 以下のメソッドを最初の Azure Function としてクラスに追加します。
[FunctionName("Add")]
public static int Add(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]
HttpRequestMessage req,
TraceWriter log)
{
int x = 1;
int y = 2;
return x + y;
}
5. メソッド定義を1つずつ見ていきましょう。最初はこのメソッドを Azure Function としてマークする FunctionName 属性です。単一のパラメータを取り、関数のパブリックな名称を表します。実際のメソッド名自体と一致する必要はありません(ここでは一致させています)。
6. 次に、メソッドは public static メソッドとしてマークされていますがこれは必須です。また、戻り値が int であることに気づくでしょう。メソッド属性を使用して別途指定されない限り、Azure Function の void でない戻り値はレンダリングされ、テキストとしてクライアントに返されます。デフォルトでは XML として返されますが、簡単に JSON に変更できます。これについては後でラボで行います。
7. 最初のパラメータには、HttpTrigger 属性が設定されています。これは、このメソッドが HTTP リクエストによって呼び出されるを示します。この属性は、メソッドの認可レベルとそれがサポートする動詞(この場合は GET のみ)も指定します。オプションで、メソッドへのパスをオーバーライドする ルート を定義し、パスから変数を自動的に抽出する方法を提供することもできます。ルートはここでは null なので、このメソッドのパスはデフォルトで /api/Add になります。
8. メソッドの最後のパラメータは、診断とエラーのメッセージを記録するために使用できる TraceWriter です。
9. メソッドの return 行にブレークポイントを設定するには、行の余白をクリックするか、行にカーソルを合わせて F9 キーを押します。
10. デバッグセッションでプロジェクトをビルドして実行するには、F5 キーを押すか、実行 > デバッグの開始 を選択します。代わりに実行ボタンをクリックしても大丈夫です。これらはすべて同じタスクを実行します。このラボの残りの部分はF5 を使用しますが、一番快適な方法で実行してください。
11. 表示 > パッド > アプリケーション出力 - AzureFunctionsLab を選択してください。
12. プロジェクトがビルドされた後、メソッドの属性とファイルの規約に基づいて、Azure Function を検出するプロセスを順を追って説明します。この場合、単一の Azure Function を検出し、1つのジョブ関数を「生成」します。
13. 起動メッセージの一番下に、Azure Function ホストは HTTP トリガー API の URL を表示します。1つだけ表示されるべきです。そのURLを新しいブラウザタブで開いてください。このラボの残りでは、Azure ポータル用に1つのタブを開き、Azure Function コードをテストするために別のタブを開いておく必要があります。
14. ブレークポイントはすぐにトリガーされます。Webリクエストは関数にルーティングされ、デバッグできるようになりました。x 変数の上にマウスを置くと値が表示されます。これで、Visual Studio デバッガの機能を完全に使用でき、プロジェクトを洗練するために必要なタスクに取り掛かることができます。
15. 以前に追加したのと同じ方法でブレークポイントを削除します(マージンをクリックするか、行を選択して F9 を押します)。
16. F5 キーを押して実行を続行します。
17. ブラウザでは、メソッドのXML結果がレンダリングされます。期待どおり、ハードコーディングされた加算演算は妥当な和を生成します。Safari で「3」だけが表示されている場合は、Safari > 環境設定 > 詳細に進み、メニューバーに"開発"メニューを表示チェックボックスをオンにしてページをリロードします。
18. Visual Studio for Mac で、停止ボタンをクリックしてデバッグセッションを終了します。テスト中のコードを変更する度に再起動(停止して実行)するのを忘れないでください。
19. Add メソッドで x と y の定義を次のコードに置き換えてください。このコードは、URLのクエリ文字列から値を抽出し、提供されたパラメータに基づいて加算を動的に実行できるようにします。
int x = int.Parse(req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "x", true) == 0)
.Value);
int y = int.Parse(req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "y", true) == 0)
.Value);
20. F5 キーを押してアプリケーションをビルドして実行します。
21. ブラウザのウィンドウに戻り、文字列 "/?x=2&y=3" をURLに追加します。URL全体は "http://localhost:7071/api/Add?x=2&y=3" になります。新しいURLに移動してください。
22. 今回は結果に新しいパラメータが反映されるはずです。異なる値で数回試してみてください。
23. デバッグセッションを停止します。
タスク4: function.jsonの操作
1. Visual Studio for Mac は、ライブラリに定義されている Azure Function のジョブ関数を「生成」していることに先ほど触れました。これは、Azure Functions が実行時にメソッド属性を実際に使用するのではなく、コンパイル時のファイルシステム規約を使用して、Azure Functions が利用可能になる場所と方法を設定するためです。ソリューションエクスプローラで AzureFunctionsLab プロジェクトノードを右クリックし、Finderで表示 を選択します。
2. bin/Debug/net461が見つかるまでファイルシステムをたどってください。Add という名前のフォルダが見つかるでしょう。このフォルダは、C# コードの関数名属性に対応するように作成されています。そのフォルダを展開すると、 function.json が見つかるでしょう。これはランタイムが Azure Function 自体をホストして管理するために使用するファイルです。コンパイル時サポートがない他の言語モデル( C# スクリプトや JavaScript など)では、これらのフォルダを手動で作成して管理する必要があります。C# 開発者向けにはビルド時に属性メタデータから自動的に生成されます。 function.json を開いてください。
3. C# の属性に精通していると、この JSON はかなり理解しやすいはずです。しかし、以前に明示的に議論していなかった項目がいくつかあります。たとえば、各 binding は direction が設定されている必要があります。ご想像の通り、in はパラメータが入力されていることを意味し、out はパラメータが戻り値( $return 経由)またはメソッドの out パラメータのいずれかであることを示します。また、アセンブリ内の scriptFile (相対パス)と entryPoint メソッド( public と static )を指定する必要があります。次のいくつかのステップでは、このモデルを使用してカスタム関数パスを追加するので、このファイルの内容をクリップボードにコピーします。
4. ソリューションエクスプローラで AzureFunctionsLabプロジェクトノードを右クリックし、追加 > 新しいフォルダーを選択します。新しいフォルダに Adder という名前を付けます。デフォルトの規約では、このフォルダ名は api/Adder などの API へのパスを定義します。
5. Adder フォルダを右クリックし、追加 > 新しいファイル... を選択してください。
6. Web カテゴリと 空の JSON ファイル テンプレートを選択します。 名前 を functionに設定し、新規(N) をクリックしてください。
7. 他の function.json の中身を新しく作成したファイルのデフォルトの中身にペーストしてを置き換えてください。
8. 最初のバインディングの最後( "name": "req" 行の後)に、以下のプロパティを追加します。前の行にカンマを含めることを忘れないでください。これにより、デフォルトのルートが上書きされ、(存在する場合)パスから int パラメータが抽出され、x 、 yという名前のメソッドパラメータに配置されます。
"route": "Adder/{x:int?}/{y:int?}"
9. また、ファイルの下部にある entryPoint プロパティを更新して、以下に示すような Add2 というメソッドを使用します。これは、パス api/Adder... が任意の名前(ここでは Add2 )を持つ適切なメソッドにマップできることを示します。
"entryPoint": "AzureFunctionsLab.Functions.Add2"
10. 最終的に function.json ファイルは次のようになります。
11. すべての作業を完了するために必要な最後のステップは、Visual Studio for Mac にこのファイルを変更するたびに出力ディレクトリの同じ相対パスにコピーするように設定することです。
12. Functions.cs に、期待される機能を実現するために次のメソッドを追加します。これは、属性を使用せず、x と y の明示的なパラメータを持つことを除けば、Add と非常に似ています。
public static int Add2(
HttpRequestMessage req,
int x,
int y,
TraceWriter log)
{
return x + y;
}
13. F5 を押してプロジェクトをビルド、実行してください。
14. ビルドが完了し、プラットフォームがスピンアップすると、新しく追加されたメソッドにマッピングされるリクエストに対して利用可能な第2のルートが存在することを示します。
15. ブラウザに戻り、 http://localhost:7071/api/Adder/3/5 にアクセスしてみてください。
16. メソッドは再び動作し、パスからパラメータを引き出し、合計を生成します。
17. Visual Studio for Mac に戻り、デバッグセッションを終了します。
タスク5: Azureストレージテーブルの操作
1. 多くの場合、構築するサービスは、これまでに構築したサービスよりはるかに複雑であり、実行にはかなりの時間とインフラストラクチャが必要になることがあります。その場合、リソースが利用可能になったときに処理待ちの要求を受け入れることが効果的であるかもしれません。Azure Functions はこれをサポートしています。他のケースでは、データを一元的に保存したい場合があります。Azure ストレージテーブルを使用すると、すぐにデータを保存できます。以下のクラスを Functions.cs に追加してください。それは名前空間の中に入るべきですが、Functions クラスの外側に記述します。
public class TableRow : TableEntity
{
public int X { get; set; }
public int Y { get; set; }
public int Sum { get; set; }
}
2. Functions クラス内で、次のコードを追加して別の関数を導入します。これは HTTP レスポンスを伴わないという点でこれまでと異なることに注意してください。最終行はパラメタや合計だけでなく、後で簡単に取得できるようにする重要な情報(PartitionKey と RowKey)が集まった新しい TableRow を返します。メソッド内のコードは、関数がいつ実行されるかを簡単に知ることができるように TraceWriter を使用します。
[FunctionName("Process")]
[return: Table("Results")]
public static TableRow Process(
[HttpTrigger(AuthorizationLevel.Function, "get",
Route = "Process/{x:int}/{y:int}")]
HttpRequestMessage req,
int x,
int y,
TraceWriter log)
{
log.Info($"Processing {x} + {y}");
return new TableRow()
{
PartitionKey = "sums",
RowKey = $"{x}_{y}",
X = x,
Y = y,
Sum = x + y
};
}
3. F5 を押してプロジェクトをビルド、実行してください。
4. ブラウザタブで http://localhost:7071/api/Process/4/6 にアクセスしてください。これにより別のメッセージがキューに入れられ、最終的に別の行がテーブルに追加されます。
5. Visual Studio for Mac に戻り、4 + 6 のリクエストのアプリケーションログを監視します。
6. 同じ URL へのリクエストを更新するためにブラウザに戻ります。今回は、Process メソッドの後にエラーが表示されます。これは、コードが既に存在するパーティションと行のキーの組み合わせを使用して、Azure テーブルストレージテーブルに行を追加しようとしているためです。
7. デバッグセッションを終了してください。
8. エラーを修正するには、TraceWriter パラメーターの直前のメソッド定義に次のパラメーターを追加します。このパラメータは Azure Functions のプラットフォームに、結果を格納するために使用していた PartitionKey の Results テーブルから TableRow を取得するように指示します。しかし、真の魔法のいくつかは、まったく同じメソッドの他の x と y パラメータに基づいて RowKey が動的に生成されていることに気づいたときに発動します。その行がすでに存在する場合、メソッドが開発者が必要とする追加作業を必要とせずに開始されたとき、tableRow はその行を持ちます。行が存在しない場合は、null になります。このような効率性により、開発者はインフラストラクチャではなく、重要なビジネスロジックに集中することができます。それ以外の場合、関数は以前と同じように継続します。
[Table("Results", "sums", "{x}_{y}")]
TableRow tableRow,
9. 以下のコードをメソッドの先頭に追加します。tableRow が null でない場合、要求された操作の結果がすでにあるため、すぐに結果を返すことができます。これはデータを返す最も堅牢な方法ではないかもしれませんが、複数のスケーラブルな層に渡る非常に洗練された操作を、少数のコードで編成できることを示しています。
if (tableRow != null)
{
log.Info($"{x} + {y} already exists");
return null;
}
-
F5 を押してプロジェクトをビルド、実行してください。
-
ブラウザのタブで、 http://localhost:7071/api/Process/4/6 で URL を更新してください。このレコードのテーブル行が存在するため、エラーを起こさずにすぐにリターンすべきです。HTTP 出力がないので、アプリケーション出力でログを見ることができます。
- http://localhost:7071/api/Process/5/7 など、まだテストされていない組み合わせを反映するように URL を更新してください。アプリケーションログのメッセージに注意してください。これは、テーブルの行が(期待どおりに)見つからなかったことを示しています。
-
Visual Studio for Mac に戻り、デバッグセッションを終了してください。
-
最後に、複数の入力レコードの扱い見てみましょう。特定の TableRow を指定するのではなく、同じ属性を使用して IQueryable を要求することができます。ランタイムは、必要な適切なリソースを使用して入力します。下記のコードを追加して、現在作業している Azure テーブルに現在存在するすべてのアイテムをリストする List 関数を作成します。また、レスポンスの MIME タイプが application/json であることを指定しているため、ランタイムは自動的にJSONとしてレンダリングします。
[FunctionName("List")]
public static HttpResponseMessage List(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]
HttpRequestMessage req,
[Table("Results", "sums")]
IQueryable<TableRow> table,
TraceWriter log)
{
return req.CreateResponse(HttpStatusCode.OK, table, "application/json");
}
-
F5 を押してプロジェクトをビルド、実行してください。
-
ブラウザのタブで http://localhost:7071/api/List にアクセスしてください。これにより、Azure テーブルのすべてのアイテムのリストがプルダウンされ、JSON としてレンダリングされます。
まとめ
このラボでは、Visual Studio for Mac で Azure Functions を作成する方法を学びました。