[vsCode C# API]データベースから取得してレスポンスするwebAPIコントローラを作る

前回、mac内にSQLServer「もどき」のRDBMSを準備しました。今回はこのデータを取得して返す仕組み作りを行います。

vsCodeの準備

開発に便利な拡張機能を準備しておきましょう。

「C#」「C# Dev Kit」をインストールしておいてください。

コントローラの作成

.NETのバージョンによっても形が違いますが、シンプルなパターンで実装したいと思います。

まずProgram.csを開き、以下に変更。※プロジェクト作成時にOpenAPIを指定していない場合はSwagger関係の記述はありません。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.MapControllers();
app.Run();

続いてModelsフォルダを作成し、その中にTest.csを作成。中身は以下。

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace プロジェクト名.Models
{
    [Table("test")]
    public class Test
    {
        public string Title { get; set; } = "";
    }
}

次にControllersフォルダを作成し、その中にTestController.csを作成。中身は以下。

using プロジェクト名.Models;
using Microsoft.AspNetCore.Mvc;

[ApiController]
    [Route("test")]
    public class TestController : ControllerBase
    {
        [HttpGet]
        public Test Get()
        {
            return new Test(){Title="test"};
        }
    }

Routeで指定したところが、コントローラの処理を呼び出す際のパスになります。そして[HttpGet]なので、今回はhttp://localhost:5249/testにアクセスするとGetが呼び出される状態。

dotnet run、もしくはDevKitを入れることで表示されたvsCodeウインドウの右上にあるデバッグ実行ボタンを押してサーバを起動しましょう。

起動するとSwagger画面にてtestの存在が確認できます。また、http://localhost:5249/testにアクセスするとjsonが表示されるはずです。

RDBMSへ接続

シンプルなコントローラの作成は完了したので、今度はDBからデータを取得するように変更します。

接続文字列の取得処理

まずは接続文字列を扱えるようにしましょう。
プロジェクトルートにあるappsettings.jsonを作成し、以下のようにします。パスワード等は適宜変更してください。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
    "MsSQLConnection": "Data Source=localhost,1433; Initial Catalog=DB名;Connect Timeout=5000;Persist Security Info=True; User ID=sa;Password=パスワード;"
  },
  "AllowedHosts": "*"
}

.envのようなものですね。続いてTestContollerもclass内部を以下のように変更。

    public class TestController : ControllerBase
    {
        private IConfiguration _configuration;

        public TestController(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        [HttpGet]
        public Test Get()
        {
            ;
            return new Test(){Title=_configuration.GetConnectionString("MsSQLConnection")??""};
        }
    }

コントローラのパラメータにIconfigurationを追加することで、先ほどのJsonにアクセスできるインスタンスを取得できます。先ほどのtestURLにアクセスすると、接続文字列が表示されます。

DBアクセス処理

まずパッケージをインストールします。

dotnet add package Microsoft.data.SqlClient
dotnet add package Dapper

先ほどのTestControllerを以下のように変更。
usingにパッケージを追加、Get内で接続しています。

using Dapper;
using プロジェクト名.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;

[ApiController]
    [Route("test")]
    public class TestController : ControllerBase
    {
        private IConfiguration _configuration;

        public TestController(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        [HttpGet]
        public Test Get()
        {
          try{
            string connectionString = _configuration.GetConnectionString("MsSQLConnection")??"";
            if(string.IsNullOrEmpty(connectionString))
              throw new NotImplementedException("接続文字列エラー");

            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                connection.Open();
                var items = connection.Query<Test>("select * from test");
                if(items.Count() >= 1)
                  return items.ToList()[0];
                else
                  throw new NotImplementedException("取得エラー");
            }
          }
          catch(Exception)
          {
            //ログ処理
            throw;
          }
        }
    }

この状態で実行すると、以下の例外が発生することがあります。

例外が発生しました: CLR/System.Globalization.CultureNotFoundException
型 'System.Globalization.CultureNotFoundException' の例外が LobieApi.dll で発生しましたが、
ユーザー コード内ではハンドルされませんでした: 
'Only the invariant culture is supported in globalization-invariant mode. 
See https://aka.ms/GlobalizationInvariantMode for more information.'

詳しくは追ってませんが言語カルチャーに関する例外の様です。数値のカンマ位置とか国によって違いますよね。

一旦この例外が発生しないようにしたいので、csprojを変更します。

    <InvariantGlobalization>false</InvariantGlobalization>

--falseに変更する。

ここがtrueになっていると発生します。

次に以下のエラーが発生した場合。

'A connection was successfully established with the server, but then an error occurred during the pre-login handshake. 
(provider: TCP Provider, error: 35 - An internal exception was caught)'

恐らくですがこれが発生する方はAzure Data Studio設定時等も発生していたと思います。SQLServerの暗号化が原因です。接続文字列の最後尾に以下を追記する必要があります。

"...;TrustServerCertificate=True"

そもそも接続できないという時は、ポート番号やパスワードをもう一度見直してみてください。

さて、これで再度サーバを起動してtestにアクセスすると、テーブルのデータが表示されているはずです。

まとめ

今回、シンプルな形で組めてよかったなって思います。.NETのいいところは学んだことが別の種類の開発でも生きてくるところ。その辺りを意識して今後も記事を書いていきます。