F# で Hello World！on Docker (Console Application / Web API)

## はじめに

以前，『[Domain Modeling Made Functional](https://www.amazon.co.jp/dp/B07B44BPFB)』という本を勧められて F# に興味は持ったものの，その時は読むのを挫折してしまいました・・・しかし，あらためて関数型ドメインモデリングに興味が湧いてきた今日この頃。何はともあれ，まずは Hello World！

(追記)
年末年始でやっと本も読めたので，そのうち何か書きたいと思います。


## Docker コンテナ
dotnet core 用の Docker イメージを使用します。

```
$ docker run --rm -it -w /home/dotnet -v $PWD:/home/dotnet mcr.microsoft.com/dotnet/core/sdk:3.1-bionic bash
```

なんか以前と場所が変わってますね。いまは[ココ](https://hub.docker.com/_/microsoft-dotnet-core/)らしいです。
参考：[公式の .NET Docker イメージ](https://docs.microsoft.com/ja-jp/dotnet/architecture/microservices/net-core-net-framework-containers/official-net-docker-images)

コンテナ内では `dotnet` コマンドが使えるようになります。
作業ディレクトリをマウントしておくことで，後ほど VS Code などで開いて編集できるようになります。


### WSL の場合

WSL の場合は docker が認識するパスとズレがあるので，先に以下のようなコマンドで `$PWD` を揃えておく必要があります。

```
export PWD=`pwd | sed -r 's/\/mnt(.*)/\1/'`
```

https://github.com/DaisukeSudo/dockers#store-some-dockerfiles


## Console Application

まずは，コンソールアプリのテンプレートを使用してプロジェクトを作成してみます。
（コンテナ側）

```
$ dotnet new console -lang "F#" -o HelloWorld
```

プロジェクトを指定して実行します。
（コンテナ側）

```
$ dotnet run --project HelloWorld
Hello World from F#!
```

無事に `Hello World from F#!` が表示されました。

カレントディレクトリに移動すれば `--project` は省略できます。


## Web API

折角なので Web API も試してみましょう。

ホスト側からアクセスするにはポートをバインドします。
テンプレートで使用されるデフォルトは 5000 と 5001 です。
（ホスト側）

```
$ docker run --rm -it -p 5000:5000 -p 5001:5001 -w /home/dotnet -v $PWD:/home/dotnet mcr.microsoft.com/dotnet/core/sdk:3.1-bionic bash
```

Web API のテンプレートでプロジェクトを作成します。
（コンテナ側）

```
$ dotnet new webapi -lang "F#" -o WeatherForecast
```

プロジェクトを指定して実行します。
（コンテナ側）

```
$ dotnet run --project WeatherForecast
```

サーバーが起動しました。（Ctrl+C で停止します）

別のターミナルからコンテナに入って確認してみます。
（ホスト側）

```
$ docker exec -it <<container_name>> bash
```

curl 叩いてみます。
（コンテナ側）

```
$ curl -kL http://localhost:5000/WeatherForecast
```

5000 (http) にアクセスすると 5001 (https) にリダイレクトされます。

- `-k` SSL のエラーを無視します
- `-L` 自動的にリダイレクト先の URL も叩いてくれます

```
[{"date":"2020-01-12T01:16:46.0608601+00:00","temperatureC":51,"summary":"Mild","temperatureF":123},{"date":"2020-01-13T01:16:46.0608821+00:00","temperatureC":32,"summary":"Cool","temperatureF":89},{"date":"2020-01-14T01:16:46.0608834+00:00","temperatureC":28,"summary":"Mild","temperatureF":82},{"date":"2020-01-15T01:16:46.0608842+00:00","temperatureC":22,"summary":"Hot","temperatureF":71},{"date":"2020-01-16T01:16:46.060885+00:00","temperatureC":2,"summary":"Cool","temperatureF":35}]
```

結果が返ってきました。
テンプレートではランダムな天気予報的なものが返ってくるようになってます。

しかし，このままではホスト側からはアクセスできません。

```
$ curl -kL http://localhost:5000/WeatherForecast
curl: (52) Empty reply from server

$ curl -kL https://localhost:5001/weatherforecast
curl: (35) gnutls_handshake() failed: The TLS connection was non-properly terminated.
```

Dockerコンテナ内のサービスが `localhost` や `127.0.0.1` で LISTEN してるとホストからアクセスできません。
`0.0.0.0` で LISTEN にする必要があります。

Properties/launchSettings.json で設定している箇所を書き換えます。

``` diff
- "applicationUrl": "https://localhost:5001;http://localhost:5000",
+ "applicationUrl": "https://0.0.0.0:5001;http://0.0.0.0:5000",
```

ついでに，https へのリダイレクトも止めます。
Startup.fs の以下の行を削除します。

``` diff
- app.UseHttpsRedirection() |> ignore
+ // app.UseHttpsRedirection() |> ignore
```

コンテナ側でサーバーを再起動すれば，ホストからもアクセスできるようになります。

```
$ curl http://localhost:5000/WeatherForecast
```

```
[{"date":"2020-01-12T12:24:14.3989892+00:00","temperatureC":53,"summary":"Bracing","temperatureF":127},{"date":"2020-01-13T12:24:14.3990139+00:00","temperatureC":36,"summary":"Warm","temperatureF":96},{"date":"2020-01-14T12:24:14.3990151+00:00","temperatureC":40,"summary":"Scorching","temperatureF":103},{"date":"2020-01-15T12:24:14.3990159+00:00","temperatureC":-16,"summary":"Bracing","temperatureF":4},{"date":"2020-01-16T12:24:14.3990167+00:00","temperatureC":27,"summary":"Freezing","temperatureF":80}]
```

## その他

以下のコマンドでテンプレートの一覧が見られます。

```
$ dotnet new --list | grep F#
```

```
Console Application                               console                  [C#], F#, VB      Common/Console
Class library                                     classlib                 [C#], F#, VB      Common/Library
Unit Test Project                                 mstest                   [C#], F#, VB      Test/MSTest
NUnit 3 Test Project                              nunit                    [C#], F#, VB      Test/NUnit
NUnit 3 Test Item                                 nunit-test               [C#], F#, VB      Test/NUnit
xUnit Test Project                                xunit                    [C#], F#, VB      Test/xUnit
ASP.NET Core Empty                                web                      [C#], F#          Web/Empty
ASP.NET Core Web App (Model-View-Controller)      mvc                      [C#], F#          Web/MVC
ASP.NET Core Web API                              webapi                   [C#], F#          Web/WebAPI
```

F# が対応しているものは全て C# でも対応している感じですね。


## つづく
次回は何かコード書いてみたいと思います。
https://qiita.com/dsudo/items/f773bf55b8a04b1a0e27