Azure Functions とは?
こちらの記事にも書いたように、WebAPIなサービスを簡単に作成することが出来る Microsoft のサーバーレス機能である。
試しにサービスを作ってみる
環境
Windows 10
Visual Studio Community 2022 (64bit) Version 17.2.5
Azureアカウント(従量課金)
プロジェクト作成
プロジェクトの追加情報は、この記事ではクライアントからのHTTP要求で起動する Http trigger で作る。
Authorization levelについては、
Function が、事前発行のAPIキーをクライアントからHTTPヘッダに設定して呼び出さないと弾くタイプなので、ここでは認証無しのAnonymousにしておく。
生成されたソースコードは既に実行可能なものになっている。
(プロジェクト作成直後は自動生成されたソースコードに赤線が引かれたりしているので、まずはビルドしておく)
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace FunctionApp1
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
return new OkObjectResult(responseMessage);
}
}
}
デバッグ実行
作成したプロジェクトをデバッグ実行(F5)してみると、コンソールが立ち上がってアクセス先のアドレスが表示される。
クライアント側のテストツールとして、ここでは VSCodeの拡張機能、REST Clientを使ってみる。
GETもしくはPOSTの Send Request をクリックすると、以下の応答が帰ってくる。
機能拡張
応答でJSONを返す
先ほどのPOSTではクライアントからの送信のみJSONだったのを、応答でもJSONを返すようにしてみる。
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace FunctionApp1
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
ResponseData response = new ResponseData(name, responseMessage);
return new OkObjectResult(response);
}
}
class ResponseData
{
public string Name { get; }
public string ResponseMessage { get; }
public ResponseData(string name, string responseMessage)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
ResponseMessage = responseMessage ?? throw new ArgumentNullException(nameof(responseMessage));
}
}
}
返すデータを表現するResponseDataを作って、そこにクライアントから来た name と、これまで応答で返していた文字列を格納し、return の OkObjectResult()の引数に渡すように変更する。
これで、先ほどのテストクライアントから送ってみると、ResponseDataがJSONに変換されていることが分かる。
応答データを Azure Storage の Blobに保存する
今度は、ResponseDataをクライアントに返すだけでなく、StorageのBlobにも保存してみる。
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Azure.Storage.Blobs;
using System.Text;
namespace FunctionApp1
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
[Blob("myblob", FileAccess.Write)] BlobContainerClient blobContainer,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
ResponseData response = new ResponseData(name, responseMessage);
await blobContainer.CreateIfNotExistsAsync();
await blobContainer.DeleteBlobIfExistsAsync("Test1Dir/ResponseData.json");
var store = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(response)));
await blobContainer.UploadBlobAsync("Test1Dir/ResponseData.json", store);
return new OkObjectResult(response);
}
}
class ResponseData
{
public string Name { get; }
public string ResponseMessage { get; }
public ResponseData(string name, string responseMessage)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
ResponseMessage = responseMessage ?? throw new ArgumentNullException(nameof(responseMessage));
}
}
}
本関数 Run() の引数にBlobへのアクセスオブジェクトを追加し、
[Blob("myblob", FileAccess.Write)] BlobContainerClient blobContainer,
Blobへの書き込み処理を追加している。
await blobContainer.CreateIfNotExistsAsync();
await blobContainer.DeleteBlobIfExistsAsync("Test1Dir/ResponseData.json");
var store = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(response)));
await blobContainer.UploadBlobAsync("Test1Dir/ResponseData.json", store);
これで、先ほどはクライアントに返していたJSONが本関数に紐づくStorageのBlobにも出力されることになる。
Microsoft Azure Storage Explorer にて
今はローカルのエミュレータで動作させているので、そのBlob Containersに、
コンテナ名:myblob
ファイルパス:Test1Dir/ResponseData.json
で保存されている。
Azureの本番サーバーに上げてみる
発行
プロジェクト右クリックから「発行」を選択する。
Azureを選択
Windowsを選択
Azure関数を作成
例)
これで関数アプリの入れ物となるリソースグループなどがAzure上に作成される。
次に、「発行」ボタンを押して作成したアプリをアップロードする。
動作確認
Azureのポータルにアクセスして、作成したリソースグループ内の「関数アプリ」を開く。
さらに「関数」を選択し、作成したアプリ(ここでは「Function1」)を開く。
「関数のURLの取得」から本関数のURLを取得する。
上でlocalhostで実行したように、VSCodeのREST Clientで動かしてみる。
出力
Azure上のストレージアカウントにもファイルが保存されている。
追加情報
Visual Studio 2017などの旧開発環境でも開発可能。
ただし、拡張機能で入っている Microsoft.NET.Sdk.Functionsとかをバージョンアップすると動作しなくなるので注意。
また、ストレージエミュレータは最新への更新が必要。(標準インストール版ではエラーが出て動作しなかった)
https://docs.microsoft.com/ja-jp/azure/storage/common/storage-use-emulator
確か、Blob関連の使用クラスも違ったはず。(CloudBlobContainerとか)