C#でgRPCサーバを実装してみた

# C#でgRPCサーバを実装してみた

## はじめに

### 背景
学生のプロジェクトで、 *Apiary*や*Swagger*などのAPIドキュメントツールを用いていたが、規模が大きくなるに連れて編集が面倒だな〜と感じていた。
↓こんな感じのyamlを書き続ける 
![image.png](https://qiita-image-store.s3.amazonaws.com/0/192927/72580cbc-234b-c5e9-dea8-4ca3c2dbbb7e.png)
そう思ってた時に出会ったのがgRPCである。

### gRPCとは
gRPCは、RPC (Remote Procedure Call) を実現するためにGoogleが開発したプロトコルの1つです。Protocol Buffers を使ってデータをシリアライズし、高速な通信を実現できる点が特長です。
ref: [gRPCって何？](https://qiita.com/oohira/items/63b5ccb2bf1a913659d6)
#### でも本当にいいなって思ったのはここじゃない！
gRPCでは、IDL（インターフェース定義言語）を使ってあらかじめAPI仕様を .proto ファイルとして定義し、そこからサーバー側＆クライアント側に必要なソースコードのひな形を生成します。
#### つまり
こんなprotoファイルを定義してあげると、APIドキュメントの生成、Client&Server用のコードを自動生成してくれる。すごい、、！
データのやり取りのロジック実装に時間を取られることがないというのが圧倒的メリットだと思ってる！

- weather.proto

```
syntax = "proto3";

package proto.weather;

service Weathers{
    rpc Get (GetRequest) returns (GetResponse) {}
}

message Weather {
    double ID = 1;
    string CityName = 2;
    double TempMax = 3;
    double TempMin = 4;
    double Wind = 5;
    string Type = 6;
    string Description = 7;
}

message GetRequest {
    string CityName = 1;
}

message GetResponse {
    Weather Weather = 1;
}
```

## 開発環境
### ツールなど
- OS: MacOS
- Visual Studio: IDE
- Dotnet SDK(2.2.103): コンテナイメージ 
- [bloomrpc](https://github.com/uw-labs/bloomrpc) : Postmanのように、GUIでAPIを叩ける有難いツール  
- Docker for Mac
- [gRPC-Trainning](https://github.com/tozastation/gRPC-Training) : 今回利用するリポジトリ

### C#のライブラリ
```
<PackageReference Include="Dapper" Version="1.50.5" />
<PackageReference Include="Google.Cloud.Language.V1" Version="1.1.0" />
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
<PackageReference Include="Grpc" Version="1.18.0" />
<PackageReference Include="Grpc.Core" Version="1.18.0" />
<PackageReference Include="Grpc.Tools" Version="1.18.0">
```
## 具体的な実装

### 事前準備
- MacOSに、protoファイルを別言語へコンパイルするためのツールとプラグインを入れておくこと  
↓とっても分かりやすいです  
ref: [C#でgRPC環境を作成する](https://qiita.com/muroon/items/4e12dde47b9e8b1e94d3)

###　ディレクトリ構成
DDD風にしてみました。
<img src="https://qiita-image-store.s3.amazonaws.com/0/192927/92b0c76b-7472-d0ed-7d35-3b84c1e084f2.png" width=50%>

### Application/Implements
主にここにRPCの実装を行う(ASP.NET CoreのControllerだと思ってもらって大丈夫です！)
流れとしては、自動生成されたファイルを基底クラスとて継承します。
あとはメソッドをオーバーライドして、ロジックを書いていく感じてす。

- Implements/WeatherImpl.cs

```
using Proto.Weather;
using System.Threading.Tasks;
using Grpc.Core;
using WeatherApi.Application.Domain.Service.Interface;
using System;

namespace WeatherApi.Application.Implements
{
    public class WeatherImpl: Weathers.WeathersBase
    {
        IWeatherService service;
        public WeatherImpl(IWeatherService _service) : base()
        {
            if (_service == null)
            {
                throw new ArgumentNullException(nameof(IWeatherService));
            }
            this.service = _service;
        }
        public override Task<GetResponse> Get(GetRequest request, ServerCallContext context)
        {
            string cityName = request.CityName;
            var weathers = this.service.FindCurrentWeatherByCityName(cityName);
            var response = new GetResponse();
            response.Weather = weathers[0];
            return Task.FromResult(response);
        }
    }
}
```

### Application/Program.cs
Implementsで作成したRPCをServerにBindします。
残りの実装は、既存のWebフレームワークと変わりません。


```
using System;
using Microsoft.Extensions.Configuration;
using WeatherApi.Application.Infrastructure;
using Grpc.Core;
using Proto.Weather;
using Proto.User;
using WeatherApi.Application.Implements;
using WeatherApi.Application.Domain.Service;
using Proto.Ping;

namespace Application
{
    public class Program
    {
        public static IConfigurationRoot configuration;
        public static SqlHandler sqlHandler;
        public static void Main(string[] args)
        {
            const int Port = 5000;
            var weatherImpl = new WeatherImpl(new WeatherService());
            var userImpl = new UserImpl(new UserService());
            var pingImpl = new PingImpl();
            Server server = new Server
            {
                Services = { 
                    Weathers.BindService(weatherImpl),
                    Check.BindService(pingImpl),
                    Users.BindService(userImpl)
                },
                Ports = { new ServerPort("0.0.0.0", Port, ServerCredentials.Insecure) }
            };
            server.Start();

            Console.WriteLine("Tozawa server listening on port " + Port);
            Console.WriteLine("Press any key to stop the server...");
            Console.Read();

            server.ShutdownAsync().Wait();
        }
    }
}
```

## セットアップ
- プロジェクトファイルのクローン  
    - `git clone https://github.com/tozastation/gRPC-Training.git`
- gRPCサーバの起動
    - `docker-compose build; docker-compose up grpc`  

## 使い方
- bloomrpcの起動　& プラスボタンから作成した `proto` ファイルを追加します
- あとは、jsonを渡すかのように書き送信するだけです
![image.png](https://qiita-image-store.s3.amazonaws.com/0/192927/308cf222-2551-90c8-6aee-e5cb70b43251.png)

## 終わりに
C#のgRPC実装があまりないので
今回の記事で、C#でのgRPCサーバ実装およびDebug方法の共有ができれば何よりです！
今後はInterceptor(Middleware)の実装もしていきたい
切実にドキュメントが増えてほしい、、、