ASP.NET CoreでWEB APIを作成してみる

ASP.NET Core

はじめに

アプリ制作をしてると、データベースとの連携をするのにAPIが必要になる
そのAPIの作成の仕方を忘備録として残しておく

使用するデータベースの作成

まず先に使用するデータベースのサンプルを作成する
構成は下記画像となる
テーブル名はusers
データベース
テーブルの作成

CREATE TABLE `users` (
  `id` int NOT NULL,
  `name` varchar(45) COLLATE utf8mb4_general_ci NOT NULL,
  `old` int NOT NULL,
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `is_deleted` tinyint NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

サンプルデータを挿入する

INSERT INTO `users` (`id`, `name`, `old`, `created_at`, `updated_at`, `is_deleted`) VALUES
(1, '斎藤さん', 30, '2024-04-07 08:58:29', '2024-04-07 08:58:29', 0),
(2, '轟さん', 33, '2024-04-07 08:58:45', '2024-04-07 08:58:45', 0),
(3, '加藤さん', 21, '2024-04-07 08:59:05', '2024-04-07 08:59:05', 0);

プロジェクトの作成

Visual Studio で新しいプロジェクトとして ASP.NET Core Web API を選択する
プロジェクト作成

ASP.NET Core Web API(native AOT) こちらは、プログラムの実行前にコードをネイティブコードにコンパイルする技術になる
パフォーマンスを向上させたりするが、利用可能なAPIや機能に制限があるので通常のASP.NET Core Web APIを使用する

今回のプロジェクト名はSampleAPIとする
プロジェクト作成

画像の状態で作成する
プロジェクト作成

NuGet から取得

今回は、DapparMysql でデータベースとの連携をする
NuGetパッケージで DapparMysql.Data を取得する
またDapper.Contribも取得しておく
Dapper.ContribCRUD(Create, Read, Update, Delete)操作を簡単に実装できるメソッドを提供しており、属性を使用してテーブル名や主キーなどを指定することができる

Dappar
Mysql

データアクセス層の構築

データベースとの連携するファイルの作成をする
まず DataAccess というフォルダを作成する
その中に IMySQLDataAccess.csMySQLDataAccess.cs の2つのファイルを作成する
下記コードとなる

public interface IMySQLDataAccess
{
    Task<List<T>> LoadData<T, U>(string sql, U parameters);
    Task SaveData<T>(string sql, T parameters);
}
using Dapper;
using MySql.Data.MySqlClient;
using System.Data;

public class MySQLDataAccess : IMySQLDataAccess
{
    private readonly string _connectionString;

    public MySQLDataAccess(string connectionString)
    {
        _connectionString = connectionString;
    }

    public async Task<List<T>> LoadData<T, U>(string sql, U parameters)
    {
        using (IDbConnection connection = new MySqlConnection(_connectionString))
        {
            var rows = await connection.QueryAsync<T>(sql, parameters);
            return rows.ToList();
        }
    }

    public async Task SaveData<T>(string sql, T parameters)
    {
        using (IDbConnection connection = new MySqlConnection(_connectionString))
        {
            await connection.ExecuteAsync(sql, parameters);
        }
    }

}

プロジェクト内は画像のようになってるはずだ
Tree

接続先データベースを設定する

接続先のデータベース情報はappsettings.jsonに記述する

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Port=3306;database=sampledata;user id=*******;password=************"
  },
  "AllowedHosts": "*"
}

接続するコードを記述

データベースを利用するためのコードをProgram.csに記述する

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

string connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); // 追加
builder.Services.AddTransient<IMySQLDataAccess>(_ => new MySQLDataAccess(connectionString)); // 追加

var app = builder.Build();

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

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

usersテーブルのクラスの作成

データの連携するためのテーブルに合ったクラスを作成する
プロジェクトに Models というフォルダを作成する
Modelsの中に UsersModel.cs を作成してコードを記述する

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

namespace SampleAPI.Models;

[Dapper.Contrib.Extensions.Table("users")]
public class Users
{
    [Dapper.Contrib.Extensions.Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required]
    [StringLength(45)]
    public string Name { get; set; }

    [Required]
    public int Old { get; set; }

    [Required]
    [Column("created_at", TypeName = "datetime")]
    public DateTime Created_At { get; set; }

    [Required]
    [Column("updated_at", TypeName = "datetime")]
    public DateTime Updated_At { get; set; }

    [Required]
    [Column("is_deleted")]
    public bool Is_Deleted { get; set; }
}

クラスのプロパティ名は命名規則によりパスカルケースを用いる
そのため、最初の文字は大文字としている
※ キャメルケースというのもある
例:firstName など

コントローラーの作成

実際にAPIにアクセスしてデータを取得するためのコントローラーを作成する
Controllersフォルダ上で右クリックをして、追加を選択
その後、コントローラーを追加する
今回は MVCコントローラー – 空UsersController.cs を作成する
まず、ユーザーを取得するコードを書いてみる

using Microsoft.AspNetCore.Mvc;
using SampleAPI.Models;

namespace SampleAPI.Controllers;

[Route("api/[controller]")]

public class UsersController : ControllerBase
{
    private readonly IMySQLDataAccess _dataAccess;

    public UsersController(IMySQLDataAccess dataAccess)
    {
        _dataAccess = dataAccess;
    }

    [HttpGet]
    public async Task<ActionResult<Users>> GetUsers()
    {
        string sql = $"SELECT * FROM `users` WHERE 1";
        var _users = await _dataAccess.LoadData<Users, dynamic>(sql, new { });
        if (_users.Count == 0)
        {
            return NotFound();
        }
        return Ok(_users);
    }

}

デバッグする

現在のプロジェクトの中身は画像のような状態になっている
プロジェクト
それではデバッグしてみよう
デバッグモードでWEB APIプロジェクトを起動すると、Swagger UIが自動的に表示される
SwaggerはAPIの仕様を簡単に確認し、テストするためのツールだ
次のようなページが表示される
Swagger

  • Users
  • WeatherForecast
    の2つが存在するが、WeatherForecastはプログラム作成時初期に作られるサンプルだ
    UsersGET /api/Usersをクリックすると次の画像のように表示されるはずだ
    apiUsers
    ここにあるTry it outをクリックして出てきたExecuteを実行してみよう
    次の画像のようになるはずだ
    Swagger

Jsonデータでデータが帰ってきてるのがわかる
さらにここに記載されてる CurlRequest URL に注目しよう
今回はプログラムを起動したままの状態でRequest URLに直接ブラウザでアクセスしてみる
Swagger
URLにアクセスをするとJsonデータが表示されてるのがわかるはずだ
このようにデータにアクセスすることができる

データを操作する 引数

次に引数を使って指定データを取得してみよう
先ほどのUsersController.csに下記を追加する

[HttpGet("{id}")]
public async Task<ActionResult<Users>> GetUsers(int id)
{
    string sql = $"SELECT * FROM `users` WHERE id = @Id;";
    var pins = await _dataAccess.LoadData<Users, dynamic>(sql, new { Id = id });
    if (pins.Count == 0)
    {
        return NotFound();
    }
    return Ok(pins);
}

再度プログラムを実行してみよう
Swagger
/api/Users/{id} という項目ができたのがわかる
id1を入れてExecuteしてみよう
Swagger
実行すると次の画像のように id 1 のデータが取得できたのがわかる
Swagger
Request URLを見るとhttps://localhost:7012/api/Users/1とidを指定してデータを取得している
では、動かしたままの状態で https://localhost:7012/api/Users/2 にブラウザで直接アクセスしてみよう
このように id 2 のデータが取得できる
Swagger

データを操作する Update

それでは次にアップデートをしてみよう
下記コードを追加する

[HttpPut("update/{id}")]
public async Task<IActionResult> UpdateUser(int id)
{
    string sql = $"UPDATE `users` SET is_deleted = 1 WHERE id = @Id;";
    await _dataAccess.LoadData<Users, dynamic>(sql, new { Id = id });
    return NoContent();
}

今まではHttpGetであったが今回はHttpPut
アップデートするときはHttpPutを使用する
それではプログラムを動かしてみよう
このようにオレンジ色でPUTが追加されたはずだ
Swagger
先ほど同様にid1を入れてExecuteしてみる
そうすると画像のようになっているはずだ
Swagger
ここで見てもらいたいのがCodeの部分だ
204になってるのがわかる
これは、サーバーがクライアントのリクエストを正常に処理したが、コンテンツを返す必要がないことを示している
実際にhttps://localhost:7012/api/Users/1にアクセスしてデータを見てみよう
Swagger
データを見てみるとis_Deletefalseからtrueに変わったのがわかる
0から1に変わったということだ
データベースにアクセスしてみると is_deleted の数値が変わっているのを確認できる
db

データを操作する Insert

次はデータを挿入してみよう
使用するのはHttpPostになる
まず、クラスをそのまま挿入できるようにするためにIMySQLDataAccess.csMySQLDataAccess.csにコードを追加する

Task InsertEntityAsync<T>(T entity) where T : class;
public async Task InsertEntityAsync<T>(T entity) where T : class
{
    using (IDbConnection connection = new MySqlConnection(_connectionString))
    {
        await connection.InsertAsync(entity);
    }
}

そしてAPIコントローラーも追加する
UsersController.csに下記コードを追加する

[HttpPost("regist")]
public async Task<IActionResult> RegistUser([FromBody] Users user)
{
    await _dataAccess.InsertEntityAsync(user);
    return NoContent();
}

これでUsersクラスを追加することができる
プログラムを動かしてみよう
画像のようにPOSTが増えてるはずだ
Swagger
開くと次のような画像になる
Swagger
ではここにデータを入れてみよう
Swagger
これでExecuteをする
Swagger
このようにデータが入ったはずだ
データベースを確認してみよう
db
ほげほげが入ってるのがわかる
これでデータをインサートすることもできるようになった

データを操作する Delete

データを削除するコントローラーを作成する
データをDeleteすることはほとんどないため、ほぼ出番のないコードになる
使用するときは注意が必要だ

[HttpDelete("delete/{id}")]
public async Task<IActionResult> DeleteUser(int id)
{
    string sql = $"DELETE FROM `users` WHERE id = @Id;";
    await _dataAccess.SaveData(sql, new { Id = id });
    return NoContent();
}

このようになってるはずだ
Swagger
それでは実際にDeleteしてみよう
実際に id3 を入れてExecuteする
Swagger
204のコードが出てきたので処理に問題ないようだ
では、データベースを確認してみよう
db
id 3 のデータが削除されてるのがわかる

さいごに

Deleteコマンドは何度も言うようにデータを削除するというのはほとんどないため、あまり使うことがないコードとなる
特に悪用されたりすることを考えるとセキュリティも強化しなくてはならなくなる
実装するときは考えて実装しよう

簡易的な用語説明

  • パスカルケース : 複数の単語を組み合わせて名前を作る際に、各単語の最初の文字を大文字で始める方法
  • キャメルケース : 最初の単語を小文字で始め、その後の各単語の最初の文字を大文字で始める方法

コメント