10yroの開発日記

福岡にある株式会社10yro(トイロ)のエンジニアが書いています

C# Pollyを使った回復力の高いAPIアクセス方法

Pollyとは、APIへのアクセス時のリトライの実装などを助けてくれるライブラリです。
Pollyを使えば、APIのアクセス時に問題が発生した場合に、自動的にリトライすることができます。
この記事では、C#でPollyを使った基本的なAPIアクセス方法について説明します。

NuGetを使ってPollyをインストール

いつものようにNuGetパッケージマネージャーから、Pollyをインストールします。
コマンドでインストールする場合は以下のコマンドを実行してください。

Install-Package Polly

HttpClientの作成

APIアクセスにはHttpClientを使用します。
以下のようにHttpClientを生成します。

var httpClient = new HttpClient();

Policyの作成

Pollyを使うためには、Policyを作成する必要があります。
Policyは、リトライや回復の方法を定義するものです。

以下は、リトライの回数を3回に設定するPolicyの作成例です。

var retryPolicy = Policy
    .Handle<HttpRequestException>()
    .RetryAsync(3);

APIアクセス

Policyを作成したら、APIにアクセスする準備が整いました。
以下は、Pollyを使ったAPIアクセスの例です。

var result = await retryPolicy.ExecuteAsync(async () =>
{
    var response = await httpClient.GetAsync("https://example.com/api/users");
    response.EnsureSuccessStatusCode();
    return await response.Content.ReadAsStringAsync();
});

この例では、HttpClientを使ってhttps://example.com/api/usersにアクセスしています。
リトライの回数が3回に設定されているため、APIアクセスに失敗した場合には、3回まで自動的にリトライされます。

リトライの条件の指定

Pollyでは、リトライの条件を指定することができます。
以下は、ステータスコードが500番台の場合にリトライするPolicyの作成例です。

var retryPolicy = Policy
    .Handle<HttpRequestException>()
    .OrResult<HttpResponseMessage>(r => (int)r.StatusCode >= 500)
    .RetryAsync(3);

このようにすることで、APIアクセスに失敗した場合に、ステータスコードが500番台の場合にはリトライするように設定することができます。

その他のPolicy

以下の公式ドキュメントを御覧ください。

github.com

まとめ

以上が、C#でPollyを使ったAPIアクセス方法の紹介です。
Pollyを使うことで、APIアクセスの際に問題が発生した場合に、自動的にリトライすることができるため、アプリケーションの回復力を高めることができます。

C# SendGridのAPIを利用してメールを送信する

何かしらプログラムからSMTPサーバー経由でメールを送信することがあるかと思いますが、Microsoft365等のサービスだとスパム対策が強化され迷惑メールに振り分けられることが多くなったようです。

今回、自社HP(Wordpress)をリニューアルした際に、問い合わせフォームから送信するメールが迷惑メールになってしまうようだったので、SendGridというメール配信サービスを利用しました。
SendGridについては以下を参照ください。
sendgrid.kke.co.jp

C#からもSendGridを利用できるようでしたのでやってみました。
SendGridのアカウントを持っていない方は、SendGridのアカウントを作成してください。
不正利用されないようにちゃんとした審査があります。

SendGridでAPI Keyの発行

  1. SendGridにログインしたら、左のメニューのSettings→API Keysをクリックします。
  2. 次に画面右上の「Create API Key」ボタンをクリックします
  3. API Key Nameを入力して、API Key PermissionはRestricted Accessを選択します
  4. Access DetailsではMail Sendの下にあるMail SendをONにします
  5. Create & ViewsボタンをクリックするとAPI Keyが生成され、パスワードが表示されますのでコピーして保存しておいてください

Visual StudioでMailkit(メール送信ライブラリ)のインストール

メール送信にMailKitというライブラリを利用します。
Visual Studioでコンソールプロジェクトを作成して、NuGetでMailKitをインストールしてください。

メール送信

具体的なメール送信プログラムは以下となります。
送信先メールアドレス、宛先メールアドレス、認証の部分のパスワードにはSendGridで生成したAPI Keyを設定してください。
実行すればメールが送信されます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp5
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var msg = new MimeKit.MimeMessage();
            msg.From.Add(new MimeKit.MailboxAddress("テスト送信先", "from@example.com"));
            msg.To.Add(new MimeKit.MailboxAddress("テスト宛先", "to@example.com"));
            msg.Subject = "テストメール";

            var text = new MimeKit.TextPart("Plain");
            text.Text = "テスト本文\r\n改行します。";
            msg.Body = text;

            using (var client = new MailKit.Net.Smtp.SmtpClient())
            {
                try
                {
                    Console.WriteLine("メール送信 start");
                    
                    // 接続
                    client.Connect("smtp.sendgrid.net", 587, MailKit.Security.SecureSocketOptions.Auto);

                    // 認証
                    client.Authenticate("apikey", "SendGridで生成したAPI Keyのパスワード");

                    // 送信
                    client.Send(msg);

                    // 切断
                    client.Disconnect(true);

                    Console.WriteLine("メール送信 end");
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }
    }
}

まとめ

C#からSendGridのAPIを使ってメール送信するプログラムをご紹介しました。
SnedGridを利用することでメールの到達率が高くなるのではないでしょうか。

弊社では一緒に働いてくれるプログラマー、エンジニアの方を募集しています。
未経験者も大歓迎ですので、ご興味ある方は以下よりお問い合わせください。

10yro.co.jp

2022年もありがとうございました

こんにちは。株式会社10yro(トイロ)の中原です。

弊社は本日2022年12月28日(水)が仕事納めとなります。
2023年は1月5日(木)からの営業となります。

しかし、一年経つのはあっという間ですね。
この前自分のSNSを見返していたら、久留米にあるミスタージョージというお店で食べたハンバーグの写真があったのですが、日付を見たら2022年2月でした。
つい最近のことだと思ってたんですが、もう10ヶ月も前のことでした。
何で久留米に行ったのか覚えてないのですが、ミスタージョージでお昼ごはんを食べて、帰りの車でメッセンジャーという芸人さんの「それゆけ!メッセンジャー」というラジオをRadikoで聞いたのを思い出します。

話は変わりますが、会社としては2022年は2期目の年でした。
2022年、会社としては以下のような活動を行いました。

2022年の活動

新規オープンのスポーツクラブの会員管理システムの開発と導入

会員管理システム自体は既存店舗で利用しているものがあったのですが、新規店舗のために機能追加を行い導入しました。
中でもAkerunというリモートロックと会員管理システムを連携する部分は大変でしたが、楽しかったです。
新規スポーツクラブの立ち上げという初めての経験でしたが、あまり大きなトラブルもなくできたかなと思っています。

BCP通知サービス(安否確認サービス)の開発とリリース

お客様からの依頼で開発元という立場でサービスの開発とリリースをしました。
また、1回のリリースで終わらず、バージョンアップも数回行いました。
まだまだ改善や新規機能追加等出てくるかと思いますので、今後もバージョンアップを繰り返してより良いサービスにしていきたいと思っています。

会社ホームページのリニューアル

10yro.co.jp

ずっと後回しになっていたのですが、会社ホームページも良いものができました。
仙台にあるインテグ様にご提案していただき、素敵なホームページを作っていただきました。

creative-integ.jp

2023年は以下の活動に力を入れて行きます。

2023年の活動予定

プログラマー、エンジニアの採用

弊社ではプログラマー、エンジニを募集しています。
未経験者でもプログラマー、エンジニアになりたいという方大歓迎です。
少しでも興味のある方は以下のお問合せフォームよりご連絡いただけると嬉しいです。

10yro.co.jp

サービス開発

上で書いたBCP通知サービスのバージョンアップに加えて、新しいサービスの開発も行う予定です。
自社サービス開発を通じて、今後力を入れていきたい技術にもチャレンジしたいと思っています。

まとめ

少し長くなりましたが、2022年もありがとうございました。
どうぞ良いお年をお迎えください。

C# Windows Hello認証の実装

みなさんご利用されていると思いますが、Windows 10や11にはWindows HelloというPINや顔認証、指紋認証の仕組みがあります。
そのWindows Helloでの認証をWindowsのアプリでも利用することができます。
今回はその実装方法についてです。言語はC#Windows Formsでサンプルアプリを実装したいと思います。

Microsoft.Windows.SDK.Contractsのインストール

Windows Helloを利用するためにNuGetで「Microsoft.Windows.SDK.Contracts」をインストールします。
いつものようにNuGetの管理画面を表示してインストールします。

インストールできました。

Formの作成

とても簡単な以下のような画面を作ります。

「WindowsHello認証」ボタンをクリックすると、Windows Helloの認証が実行され、
認証OKの場合にはメッセージダイアログで「OK」と表示し、
認証NGの場合にはメッセージダイアログで「NG」と表示します。

Windows Hello認証の実装

実装もとても簡単で、以下のような実装になります。

using System;
using System.Windows.Forms;

namespace WindowsHelloSample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// ボタンクリック時の処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void button1_Click(object sender, EventArgs e)
        {
            var available = await Windows.Security.Credentials.UI.UserConsentVerifier.CheckAvailabilityAsync();
            if (available == Windows.Security.Credentials.UI.UserConsentVerifierAvailability.Available)
            {
                var result = await Windows.Security.Credentials.UI.UserConsentVerifier.RequestVerificationAsync("Sampleアプリの認証を行います");
                if (result == Windows.Security.Credentials.UI.UserConsentVerificationResult.Verified)
                {
                    MessageBox.Show("OK");
                } else
                {
                    MessageBox.Show("NG");
                }
            } else
            {
                MessageBox.Show("Windows Helloに対応していない端末です。");
            }
        }
    }
}

Windows.Security.Credentials.UI.UserConsentVerifier.CheckAvailabilityAsync()Windows Helloに対応しているかをチェックし、チェックOKの場合にWindows.Security.Credentials.UI.UserConsentVerifier.CheckAvailabilityAsync()で認証を実行し、戻り値がVerifiedであれば認証OKとなります。

動かしてみる

ボタンをクリックするとWindows Helloのダイアログが表示され、PIN入力または顔認証、指紋認証を行うことができます。

まとめ

認証機能があるWindowsアプリを開発する際は、Windows Hello認証機能も入れてみてはいかがでしょうか。
Windowsと同じように毎回パスワードを入力する手間が省けるので、便利だと思います。

PHP LaravelでRESTful API開発 その2

前回の続きです。
前回の記事は以下です。

dev.10yro.co.jp

前回はControllerにPOSTメソッドの作成まででしたので、今回はその他のメソッドを実装したいと思います。

目次

Bookテーブルのレコードを取得する(GETメソッドの実装)

Bookテーブルから全件取得したいと思います。
Bookテーブルには以下の2レコードが登録されています。

ControllerのgetAllメソッドを以下のように修正します。

<?php
    /**
     * すべてのbookを取得
     *
     * @return object $result
     */
    public function getAll()
    {
        $books = Book::get()->toJson(JSON_PRETTY_PRINT);
        return response($books, 200);
    }

postmanで呼び出すと、以下のように取得結果がJson形式で返ってきます。

Bookテーブルのレコードをキー指定で1件取得する(GETメソッドの実装)

ControllerのgetByIdメソッドを以下のように修正します。

<?php
    /**
     * IDを指定して1件取得
     *
     * @param integer $id
     * @return Bool $result
     */
    public function getById(int $id)
    {
        $books = Book::find($id)->toJson(JSON_PRETTY_PRINT);
        return response($books, 200);
    }
?>

postmanで呼び出すと、以下のように取得結果がJson形式で返ってきます。

Bookテーブルのレコードを更新する(PUTメソッドの実装)

ControllerのupdateByIdメソッドを以下のように修正します。

<?php
    /**
     * Bookを更新
     *
     * @param Request $request
     * @param integer $id
     * @return void
     */
    public function updateById(Request $request, int $id)
    {
        if (Book::where('id', $id)->exists())
        {
            $book = Book::find($id);
            if (!empty($request->title)) {
                $book->title = $request->title;
            }
            if (!empty($request->author)) {
                $book->author = $request->author;
            }
            if (isset($request->memo)) {
                $book->memo = $request->memo;
            }
            $book->save();

            return response()->json([
                'message' => 'updated book'
            ], 201);
        } else {
            return response()->json([
                'message' => 'book not found'
            ], 404);
        }
    }

postmanで呼び出すと、以下のように正常終了したメッセージが返ってきました。

テーブルを見るとid = 1のレコードが更新されています。

Bookテーブルのレコードを削除する(DELETEメソッドの実装)

ControllerのdeleteByIdメソッドを以下のように修正します。

<?php
    /**
     * Bookを削除
     *
     * @param integer $id
     * @return void
     */
    public function deleteById(int $id)
    {
        if (Book::where('id', $id)->exists())
        {
            $book = Book::find($id);
            $book->delete();

            return response()->json([
                'message' => 'updated book'
            ], 202);
        } else {
            return response()->json([
                'message' => 'book not found'
            ], 404);
        }
    }

postmanで呼び出すと、以下のように正常終了したメッセージが返ってきました。

テーブルを見るとid = 2のレコードが削除されています。

まとめ

Laravelを使って基本的なAPIの機能を実装してみました。
データベースへのアクセス等も簡単に実装できるので、とても良かったです。
今までPHPは深くやったことなかったのですが、今後はちゃんと使っていきたいと思いました。

今回のソースコード一式は以下に置いています。
github.com

PHP LaravelでRESTful API開発 その1

今回はPHPフレームワークLaravelを利用して、RESTful APIのサンプルを作成したいと思います。
まずは1回目として、Laravelプロジェクトの作成、利用するDBの作成、Modelの作成、Controllerでpostメソッドの実装までをやっていきます。

目次

MySQLにデータベースを作成

まず、APIで利用するデータベースを作成します。
今回はMySQLを利用します。

MySQL Workbenchを利用してデータベースを作成します。

SCHEMASを右クリック→Craeta Schema...をクリック

Nameにapi_sampleと入力して、Applyをクリック

以下の画面でApplyをクリック

以下の画面でFinishをクリック

SCHEMASにapi_sampleが作成されます

Laravelのプロジェクトを作成

プロジェクトを作成するフォルダまで移動して以下のコマンドを実行します。

laravel new api-sample

以下のコマンドで起動してみます。

php artisan serve

以下のように表示されればOKです。

ブラウザで http://127.0.0.1:8000/ にアクセスすると以下のような画面が表示されます。

ModelとMigrationを作成

先程作成したDBにテーブルを作るため、ModelとMigrationファイルを作成します。
今回は本のタイトルや著者などを管理するテーブル「book」を作成します。

以下のコマンドを実行します。

 php artisan make:model Book -m

以下のようにapp/ModelsにBook.php、database/migrationsに2022_11_24_053031_create_books_table.phpというファイルが作成されました。

作成されたmigrationのファイルを見てみます。
upメソッドでテーブルを生成する処理(create table)が実行されます。
デフォルトではidとtimestampsが定義されていますので、必要に応じてこのメソッド内にテーブルのカラムの定義を記述します。
timestampsはcreated_at, updated_atの2つのカラムが生成されます。

<?php
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('books', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }

downメソッドでは、テーブルを削除する処理(drop table)が実行されます。
upメソッドで行った変更を元に戻す処理を記述することとなります。

<?php
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('books');
    }
};

upメソッドを以下のように修正します。
title, author, memoという文字列のカラムを追加しました。
memoについてはnull許可としています。

<?php
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('books', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('author');
            $table->string('memo')->nullable();
            $table->timestamps();
        });
    }

次はModelを以下のように修正します。
protected $fillableの記述を追加しました。

<?php
class Book extends Model
{
    use HasFactory;

    protected $fillable = [
        'title',
        'author',
        'memo'
    ];
}

.envにDB接続先情報を設定

プロジェクトのルートにある.envファイルのDB接続先情報を修正します。

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=api_sample
DB_USERNAME=<your user name>
DB_PASSWORD=<your password>

Migrationの実行

コマンドプロンプトで以下のコマンドを実行し、DBにBookテーブルを作成します。

php artisan migrate

以下のように実行結果が表示されます。
(book以外はlaravelのデフォルトで含まれるmigrationファイルですので、不要であれば実行前に削除してください)

MySQL Workbench等のツールでDBを確認するとbookテーブルが生成されていると思います。

Controllerの作成とRouteの設定

次はControllerを作成します。
以下のコマンドを実行してBookControllerを作成します。

php artisan make:controller BookController

app/Http/Controllersの下にBookController.phpが作成されます。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class BookController extends Controller
{
    //
}

取得、追加、更新、削除のメソッドを追加します。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class BookController extends Controller
{
    /**
     * すべてのbookを取得
     *
     * @return object $result
     */
    public function getAll()
    {
        return "Called getAll";
    }

    /**
     * IDを指定して1件取得
     *
     * @param integer $id
     * @return Bool $result
     */
    public function getById(int $id)
    {
        return "Called getById";
    }

    /**
     * Bookを追加
     *
     * @param Request $request
     * @return void
     */
    public function create(Request $request)
    {
        return "Called create";
    }

    /**
     * Bookを更新
     *
     * @param Request $request
     * @param integer $id
     * @return void
     */
    public function updateById(Request $request, int $id)
    {
        return "Called updateById";
    }

    /**
     * Bookを削除
     *
     * @param integer $id
     * @return void
     */
    public function deleteById(int $id)
    {
        return "Called deleteById";
    }
}

routes/api.phpに以下を追加します。

<?php

Route::get('book', [BookController::class, 'getAll']);
Route::get('book/{id}', [BookController::class, 'getById']);
Route::post('book', [BookController::class, 'create']);
Route::put('book/{id}', [BookController::class, 'updateById']);
Route::delete('book/{id}', [BookController::class, 'deleteById']);
Route::get('book', [BookController::class, 'getAll']);

例えば上記記述をすることで、http://127.0.0.1:8000/api/bookのgetメソッドにアクセスすると、BookControllerのgetAllメソッドが呼び出されることになります。

ここまで終わったら、一度確認のため動かしてみます。
以下のコマンドを実行してください。

php artisan serve

Laravelが起動したら、ブラウザで http://127.0.0.1:8000/api/book アクセスしてみます。
以下の文字が表示されればOKです。

Bookテーブルにレコードを作成(postメソッドの実装)

postメソッドが呼ばれたときに、bookテーブルにレコードを作成します。
コントローラーのcreateメソッドを以下のように修正します。

<?php
    /**
     * Bookを追加
     *
     * @param Request $request
     * @return void
     */
    public function create(Request $request)
    {
        $book = new Book();
        $book->title = $request->title;
        $book->author = $request->author;
        $book->memo = $request->memo;
        $book->save();

        return response()->json([
            'message' => 'created book'
        ], 201);
    }

Postmanというツールでpostメソッドを呼び出します。 以下はPostmanの画面です。

以下のように呼び出した結果がBodyに表示されたらOKです。

Bookテーブルの中身を確認すると、ちゃんとレコードが作成されています。

まとめ

LaravelでAPIの実装どうするんだろ?と思ってましたが、他のサイトを参考にしながらですが、ここまでは順調にできました。
Laravel便利すぎです。 次回はその他メソッドの実装を行っていきたいと思います。

C# PCSC Sharpを使ってICカードのIDを読み取る

今回はC#ICカードに入っているIDを取得する方法についてです。
ICカードリーダーとの連携は1から実装するのは気が遠くなりそうなので、今回はPCSC Sharpという以下のライブラリを利用しました。
ICカードはマイフェア(Myfair)、フェリカ(Felica)を想定しています。

github.com

PCSC Sharpのインストール

まずWindows Formsの新規プロジェクトを作成します。
その後にNugetでPCSC、PCSC.Iso7816をインストールしましょう。

プロジェクトを右クリック→「Nugetパッケージの管理」をクリックします。
参照で「pcsc」と入力して、「PCSC」「PCSC.Iso7816」をインストールします。

インストールされました。

画面

今回は以下のような簡単な画面を作成しました。
「モニター開始」をクリックすると、カードリーダーを監視し、ICカードを認識したらボタンの下にある欄(リストボックス)に取得したIDを表示します。

初期処理

初期処理として、画面ロード時にICカードリーダーの存在チェックを行います。 11行目でPCに接続されているICカードリーダーを取得して、その中から対象となるICカードリーダーが存在するかチェックしています。

モニター開始

モニター開始ボタンをクリックすると、ICカードリーダーにICカードがかざされるのをモニターします。
ICカードを検知する(ICカードリーダーの状態が変わる)と、27行目のStatusChangedというイベントが発火されますので、そのイベントにMonitor_StatusChangedというメソッドを登録しています。
このメソッド内でIDを取得してリストボックスにIDを表示します。

実際にIDを取得するメソッドは以下となります。

実行してみる

以下の動画のようにモニター開始後にリーダーにカードをかざすと、IDを読み取ってくれます。

PCSCSharpを使うと簡単に実装できました。
ソースコードは以下に置いています。

github.com

弊社では一緒に働いてくれる仲間を募集しています。
ご興味ある方はご連絡ください! 10yro.co.jp