Web APIで複数のプロパティに対してバリデーションをかけたい

以前のプロジェクトでやったこと。

ASP.NET CoreのWeb APIでデータクラスを受け取った際にValidationをやりたい。そういうケースは当然あり、ASP.NET Coreにも検証機能が備わっています
ASP.NET Coreに任せると[Required]属性などを付与したプロパティでValidationをかけることができますが、複数のValidation条件の中で一つNGを検出したらそこで処理が中断されるため、1回のリクエストに対する検証項目が1つだけになってしまう。

マスタの登録画面で保存ボタンを押した際など、1度のリクエストで同時に複数の項目へのValidationを行いたいという状況がありました。

次のことをやっていました。

  1. 検証用Attributeクラスを作る。
    Required、Range、大小比較の小さいほう、大きいほうなどを作りました。
  2. 検証対象のデータクラスのプロパティに検証用Attributeを付与。
  3. 自前の抽象クラス Validatable を作り、検証対象のデータクラスはこれを継承させる。
  4. Validatableクラスは void Validate() メソッドを持ち、呼び出されれば検証用Attributeの付与されたプロパティに対し全チェックを行う。
  5. チェック結果に1つ以上問題があれば、検証エラーのリストを乗せた自作Validation例外をスロー
  6. ExceptionFilterを自前で実装し、Validation例外を受けて検証エラーのリストを乗せたエラーレスポンスを作って返す
  7. Controllerでは受け取ったデータクラスに対し Validate()メソッドを呼び出すことで検証を行う

悪いプラクティスを含んでいる可能性もありますのでご注意(ご指摘ただければ幸いです)。
ソースはそのうち追加するかも。

ASP.NET Core, EF Core, PostgreSQLのテストをGitLab CIで回す

以前別のブログに書いた記事のため
フレームワークバージョンが古いことにご注意ください

EF Coreを利用したCIをGitLabで回そうとした際にRDBコンテナとの接続のやり方が分からず嵌りましたが、とりあえずできたので書き残しておきます。より適切なやり方があるかもしれません。 私の環境で、フレームワーク等は以下のバージョンを使いました。

やりたいこと

やること

  1. ASP.NET Coreプロジェクトを作成
  2. EF Coreモデルを作成
  3. テストを作成
  4. GitLab CIの設定ファイルを作成

1. ASP.NET Coreプロジェクトを作成

プロジェクト名・ソリューション名はSampleApiとします。 WebApiとしてプロジェクトを作成しますが、本記事の範囲では何でも構いません。 さらにテストプロジェクトSampleTestを追加します。

mkdir SampleApi
cd SampleApi
dotnet new sln
dotnet new webapi -o SampleApi
dotnet new xunit -o SampleTest
dotnet sln add SampleApi SampleTest

作成できるプロジェクトはインストールされたSDKバージョンに依存します。

2. EF Coreのモデルを作成

適当なモデルをSampleApiプロジェクトに追加します。

public class User
{
  [Key]
  public long Id { get; set; }

  public string Name { get; set; }
}

上のモデルを含むDbContextを作成します。 接続文字列を環境変数から拾うようにしています。

public class SampleDbContext : DbContext
{
  public DbSet<User> Users { get; set; }

  protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  {
    var connectionString = Environment.GetEnvironmentVariable("DB_CONNECTION_STRING");
    optionsBuilder.UseNpgsql(connectionString);
  }
}

NugetでNpgsql.EntityFramework.Coreを取得する必要があります。

3.テストの作成

テストプロジェクトにテストを追加します。 今回はEF Coreを使いデータを突っ込んで取り出すだけのテストです。

public class UnitTest1
{
  [Fact]
  public void Test1()
  {
    //arrange
    var sut = new SampleDbContext();
    var id = 1;
    var user = new User() {Id = id, Name = "Alice"};

    //act
    sut.Users.Add(user);
    sut.SaveChanges();
    var result = sut.Users.First(u => u.Id == id);

    //assertion
    Assert.Equal(user.Id, result.Id);
    Assert.Equal(user.Name, result.Name);
  }
}

4. GitLab CIの設定ファイルを作成

GitLab CIを動かすために .gitlab-ci.yml を追加します。 PostgreSQLコンテナの設定はGitLab公式のUsing PostgreSQLを参考にしました。 DB_CONNECTION_STRINGは上のSampleDbContextで使う環境変数で、PostgreSQLコンテナの設定に合わせます。

stages:
- test

test:
  stage: test
  image: microsoft/dotnet:2.2-sdk
  services:
  - postgres:11.1
  variables:
    POSTGRES_DB: nice_marmot
    POSTGRES_USER: runner
    POSTGRES_PASSWORD: "admin1234"
    DB_CONNECTION_STRING: "Host=postgres;Username=runner;Password=admin1234"
  script:
  - cd SampleApi
  - dotnet restore
  - cd SampleApi
  - dotnet ef migrations add InitialCreate
  - dotnet ef database update
  - cd ..
  - dotnet test

以上をpushするとパイプラインが走り、マイグレーション、テストまで動いてくれました。

Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.

Raspberry pi クラスタ上でAmbassador Patternを動かそうとしてはまる

コンテナの利用パターンを学ぶために 分散システムデザインパターン を読んでいます。
www.oreilly.co.jp

3章アンバサダーパターンより。

シャードしたRedisキャッシュに対するプロキシとして twemproxyをアプリケーションコンテナのPodに配置する設定が出てきますが、私の環境では正常に動作しませんでした。

$ kubectl logs ambassador-example twemproxy
standard_init_linux.go:211: exec user process caused "exec format error"

結論から言うと、RaspberryPiはarmアーキテクチャのため使用しているイメージが動かなかったようでした。
https://hub.docker.com/r/ganomede/twemproxy/
書籍ではこちらのイメージを利用していますが、ここのDockerfileをそのまま自環境でビルド・プッシュして、プッシュしたイメージを利用すれば解決しました。
プッシュ先はGitLabのコンテナレジストリを使いました。

Raspberry pi 4 によるKubernetesクラスタ [2020/07]

家にあるraspberry pi 4の 3台をもってクラスタを構成しました。  

環境構築にあたっては、こちらを参考にさせていただきました。
Raspberry PiでおうちKubernetes構築【物理編】
Raspberry PiでおうちKubernetes構築【論理編】

基本的に記事の通り。

raspberrypi でswapfileを使わない設定をするために

sudo dphys-swapfile swapoff 

をやった後で、再起動後に設定が消えてる気がするので

sudo systemctl disable dphys-swapfile

をやった。