RestAPIとは?
今回はAzure(アジュール)を利用してRestAPI(レストエーピーアイ)を作成します。
RestAPIというのをざっくり説明するとURLにアクセスすると決まった形式で
情報を教えてくれる機能です。
例えば下記のURLにアクセスするとコロナウィルス の
都道府県ごとの症例数と死亡数の合計を、座標を含めてデータを
リターンしてくれます。
COVID-19 Japan Web API
https://covid19-japan-web-api.now.sh/api/v1/prefectures
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
[ { "id": 1, "name_ja": "北海道", "name_en": "Hokkaido", "lat": 43.46722222, "lng": 142.8277778, "population": 5248552, "last_updated": { "cases_date": 20200526, "deaths_date": 20200526, "pcr_date": 202059 }, "cases": 1052, "deaths": 84, "pcr": 8676 }, { "id": 2, "name_ja": "青森", "name_en": "Aomori", "lat": 40.78027778, "lng": 140.83194440000003, "population": 1246138, "last_updated": { "cases_date": 20200526, "deaths_date": 20200526, "pcr_date": 202058 }, "cases": 27, "deaths": 1, "pcr": 749 }, -----省略------ |
これらの情報をプログラムから取得し、グラフや表に加工して
情報サイトを作成したりするのですが、今回はそのサイトではなく、
URLにアクセスすることでデータを返すRestAPIをAzureを利用して作成してみます。
Azureとは便利ツールの集合体
Azureはマイクロソフト社が提供しているクラウドサービスで
仮想サーバーやデータベース、AIプラットフォーム等、様々な
サービスをクラウドで利用できるようにした物です。
今回はRestAPIを作成するために、下記のサービスを利用しています。
・WebApp
・Functions
・SQL Server
・SQL Database
上記のサービスは無料プランで使用することができますので、
事前に各サービスを作成しておいてください。
Linuxで開発環境の構築
今回はUbuntu20.4を利用して開発を行います。
ターミナルを開き、以下のコマンドを実行して、
Microsoftのキーとフィードを登録します。
その後.NET(ドットネット)のSDKをインストールします。
1 2 3 4 |
wget -q https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb sudo apt-get install apt-transport-https sudo apt-get update sudo apt-get install dotnet-sdk-3.1 |
パッケージ dotnet-sdk-3.1 が見つかりません” のようなエラー メッセージが表示される場合は
下記コマンドを実行してもう一度インストールしてみてください。
1 2 3 |
sudo dpkg --purge packages-microsoft-prod && sudo dpkg -i packages-microsoft-prod.deb sudo apt-get update sudo apt-get install dotnet-sdk-3.1 |
インストールが無事に完了したらWebAPI用のファイルを作成します。
1 |
$ dotnet new webapi -o sample-rest-api |
プロジェクト名のフォルダが作成されていればOKです。
WebAppの動作を確認してみよう
前の手順で作成したフォルダをVisualStudioCodeで開きます。
作成されたフォルダの中には「WeatherForecastController.cs」という
ファイルがあるのでデバッグで実行してみます。
実行すると下記の様にちょっと怪しい画面が表示されますが
これはローカル(自分のPC)にアクセスしているだけなので問題ありません。
詳細情報のボタンをクリックしてページを開いていきましょう。
ページを開いた後、URLを下記に変更すると
次のような画面が情報が表示されるはずです。
https://localhost:5001/weatherforecast
サンプルのプログラムは日付、気温、暖かさがランダムで表示されるRestAPIです。
それでは少しプログラムの中をみてみましょう。
RestAPIはルートが大事!
WeatherForecastController.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; namespace sample_rest_api.Controllers { [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [HttpGet] public IEnumerable<WeatherForecast> Get() { var rng = new Random(); return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }) .ToArray(); } } } |
ごちゃごちゃしてわかりにくいですが、
重要なのは11行目の『[Route(“[controller]”)]』と言う部分です。
Route(ルート)というのは経路とか道順と言う意味ですよね。
道順は[controller]の次の行(12行目)で定義しているクラス名から
「Controller」の部分を除いたものでアクセスすることが可能になります。
WeatherForecastController
つまりWeatherForecastというURLのパラメータでアクセスできると言うことです。
https://localhost:5001/weatherforecast
次はパラメータを追加してみましょう。
WeatherForecastController.csにクラスを追加します。
40-44行目に文字列を受けた場合は「Hello World」と出力
されるようにプログラムを変更しました。
※変数「a」のなかにURLで追加した文字列が格納されます
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; namespace sample_rest_api.Controllers { [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [HttpGet] public IEnumerable<WeatherForecast> Get() { var rng = new Random(); return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }) .ToArray(); } [HttpGet("{a}")] public ActionResult<string> Get(string a) { return "Hello World"; } } } |
結果を確認してみます。
無事に結果が表示されました。
つぎはAzureにデプロイします。
Azureにデプロイしてみる
続いてAzureにデプロイしてみます。
デプロイと言うのは「配置する」と言うことです。
環境設定で作成したAzureのWebAppにプログラムを配置していきます。
AzureにデプロイするためVisualStudioコードの拡張機能を利用します。
今回はAzure App Serviceを利用するため、Visual Studio Codeから
インストールし、Azureのアカウントでサインインしておきます。
ここまでできたら、Azureにデプロイするためのファイルを作成するため、
次のコマンドをターミナルから入力します。
1 |
dotnet publish -c Release -o ./deployFile |
フォルダが作成されたら右クリックしてDeployを選択します。
デプロイ完了後、デプロイ先のURLにアクセスした際に、
ローカルで実行した時と同じ結果が表示されればOKです。
SQL Databaseとつなげる
つづいて、WebAppとDatabase(データベース)を接続していきます。
Databaseへの接続のため、データベースの構造をプログラムで扱えるようにするため
専用のファイルを作成しますが、このファイルはツールで自動生成することが可能です。
まずはこのファイルを作成するためのツールをインストールします。
1 2 3 |
$ dotnet tool install --global dotnet-ef $ dotnet add package Microsoft.EntityFrameworkCore.SqlServer $ <span class="hljs-keyword">dotnet</span> <span class="hljs-keyword">add</span> package Microsoft.EntityFrameworkCore.Design |
インストールが完了したら次のコマンドでデータベース接続用のファイルを
作成します。<ID>,<PASS>,<SERVERNAME>等は適宜書き換えてください。
Modelと言うフォルダにデータベースの情報が記載されたファイルが作成されます。
1 |
dotnet ef dbcontext scaffold "Server=tcp:<SERVERNAME>.database.windows.net,1433;Database=<DBNAME>;User ID=<ID>;Password=<PASSWORD>;Encrypt=true;Connection Timeout=30;" Microsoft.EntityFrameworkCore.SqlServer -o Models -c SampleDbConnection |
作成が完了したらappsetting.jsonに接続情報を追加します。
1 2 3 4 |
"AllowedHosts": "*", "ConnectionStrings": { "DefaultConnection": "Server=tcp:<SERVERNAME>.database.windows.net,1433;Database=<DBNAME>;User ID=<ID>;Password=<PASSWORD>;Encrypt=true;Connection Timeout=30;" } |
次にsample-rest-apiのフォルダに格納されているStartup.csに記述されている
ConfigureServicesメソッドを編集します。
sample-rest-apiの中でservices.AddDbContextメソッドでデータベースにアクセスするクラスを初期化します。
SampleDbConnectionクラスは、データベースにアクセスするために必要なクラスで、
DefaultConnectionは、appsettings.jsonに記述した接続文字列を利用することを表しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//追加using部 using sample_rest_api.Models; using Microsoft.EntityFrameworkCore; //省略 public void ConfigureServices(IServiceCollection services) { services.AddControllers(); // 追加 services.AddDbContext<SampleDbConnection>(options => { options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")); }); } |
次に、データベースから情報を取得するコードを書いていきます。
Controllerフォルダに、DbController.csファイルを追加し以下のようにします。
DbController.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using sample_rest_api.Models; namespace sample_webapi.Controllers { [Route("api/[controller]")] [ApiController] public class DbController { private readonly SampleDbConnection _dbContext; public DbController(SampleDbConnection dbContext) { _dbContext = dbContext; } [HttpGet()] public async Task<JsonResult> Get() { // usersテーブルから、最初の10件を取得する var result = await _dbContext.Users.Take(10).ToListAsync(); // 取得結果をJSONにして返却する return new JsonResult(result); } } } |
ここまでできたらデプロイして確認しましょう。
あらかじめDBに登録した情報が10件表示されればOKです。
次はFunctionsでAPIを作成してみます。
AzureFunctiosファイルの作成
次にAzureのFunctionsを利用してWebAPIを作成してみましょう。
AzureのFunctionsのファイルもツールを用いてテンプレートを作成できますので、まずは
RestAPI用のHttpTriggerのテンプレートをインストールします
1 2 |
$ dotnet new --install Microsoft.Azure.WebJobs.ItemTemplates $ dotnet new --install Microsoft.Azure.WebJobs.ProjectTemplates |
テンプレートのインストールが終わったら
次のコマンドでFunctionsのテンプレートファイルを作成します。
1 2 3 |
$ dotnet new func -o azr-functions $ cd azr-functions $ dotnet new http -o Function1 |
作成されたFunction1.csを開き下記のようになっていればOKです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
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 Company.Function { public static class Function1 { [FunctionName("Function1")] public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "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); } } } |
このプログラムはgetでNameクエリを受けた時またはPOSTでデータを受けた場合に
“Hello, [入力された値]. This HTTP triggered function executed successfully.”
の文字を加えて画面に表示します。
実際に確認してみましょう。
無事に動作が確認できました。
それではFuntionとデータベースを連携させでみましょう。
Function1と同じようにテンプレートファイルを作成するコマンドをりようして
Function2ファイルを作成します。
1 |
$ dotnet new http -o Function2 |
データベースと接続するためにはデータベースの構造情報が必要ですので、
WebApp作成の時にに作成したModelsフォルダとStartup.csを流用します。
流用の方法は単純で、ModelsフォルダとStartup.csをazr-functionsフォルダにコピーするだけでOKです。
Startup.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
using System; using Microsoft.Azure.Functions.Extensions.DependencyInjection; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using sample_rest_api.Models; [assembly: FunctionsStartup(typeof(Company.Function.Startup))] namespace Company.Function { class Startup : FunctionsStartup { public override void Configure(IFunctionsHostBuilder builder) { var config = builder.Services.BuildServiceProvider().GetRequiredService<IConfiguration>(); builder.Services.AddDbContext<SampleDbConnection>( options => options.UseSqlServer(config.GetConnectionString("DefaultConnection")) ); } } } |
Functions2.csファイルはgetでnameパラメータを取得し、データベースからnameの文字列に一致する
レコードだけを抽出するように変更しました。(29-40行目)
Functions2.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
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 sample_rest_api.Models; using System.Linq; using Microsoft.EntityFrameworkCore; namespace Company.Function { public class Function2 { private readonly SampleDbConnection _dbContext; public Function2(SampleDbConnection dbContext) { _dbContext = dbContext; } [FunctionName("Function2")] public async Task<JsonResult> Run( [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req, ILogger log) { log.LogInformation("C# HTTP trigger function processed a request."); // nameパラメータの文字列を取得 string name = req.Query["name"]; if (string.IsNullOrEmpty(name)) { return new JsonResult("nameを指定してください。"); } // 指定された文字が含まれるnameカラムの行を取得する var result = await _dbContext.Users.Where(x => x.Name.Contains(name)).ToListAsync(); // 取得結果JSONで返却する return new JsonResult(result); } } } |
これで作成は完了です。
デプロイする前にローカル環境で動作確認を行いますが、
ローカル環境で実行する際に接続文字列が必要なため、local.settings.jsonに
接続文字列を追加します。
1 2 3 |
"ConnectionStrings": { "DefaultConnection": "Server=tcp:<DBNAME>.database.windows.net,1433;Database=<DBNAME>;User ID=<ID>;Password=<PASSWORD>;Encrypt=true;Connection Timeout=30;" } |
ここまで完了したらデバッグから動作を確認します。
デバッグ画面から「Attach to .Net Function」を選択すればブラウザが
自動的に立ち上がります。
ブラウザが立ち上がったらアドレスを入力して
動作を確認します、
「http://localhost:7071/api/Function2?name=06」
データベースに登録された内容が表示されていればOKです。
下記の場合、URLに「06」を指定しているため、nameに06を持つuser06が抽出されている
ことが確認できます。
デバッグ画面で「Attach to .Net Function」が表示されない場合は下記の
拡張機能をインストールして試してみてください。
Functionsをデプロイする
動作の確認ができましたので、Azureにデプロイしていきます。
Visual Studio Codeのコマンドパレットから「Azure Functions: Deploy to Function App…」を
選択します。
次に、Azure用の設定として、Functionsの
構成ページからデータベースの接続文字列を設定します。
ここまでできたら実際にURLにアクセスして確認します。
デプロイした関数は認証キーがURLに付加されるので
各関数の概要にある「関数のURLの取得」から公開URLを確認して
アクセスしてください。
結果が表示されればOKです。
今回はAzureのWebAPIとFunctionsを利用してRestAPIを作成しました。
RestAPIを利用することで様々なプラットフォームで多様なアプリケーションを
構築することができるので是非作成してみてください。