.NET Core 2.0 で EC-CUBE API を作ろう

EC-CUBE3 になって、 [Web API のプラグイン](https://www.ec-cube.net/products/detail.php?product_id=1116) が登場しました。
でも、 **まだβ版だから不安** とか、 **うちは2系なんだけど** という方も多いはず。

EC-CUBE で API の開発をする上で、難易度が高いのが認証部分。
商品データを扱う認証不要な API を作るのは簡単ですが、 API で扱いたいのは、注文や顧客データのような、機密性の高いデータである場合が多いです。

EC-CUBE3 の API プラグインでは、[OAuth2.0/OpenID Connect の認証](http://doc.ec-cube.net/api_authorization.html) を採用していますが、 PHP ではサポートしているライブラリも少なく、[実装がものすごく大変](https://www.slideshare.net/nanasess/eccube-api-83741818)です。

2016年に .NET Core がオープンソースとしてリリースされ、 Windows だけではなく、 Linux や Mac の環境でも利用できるようになりました。
また、 C# は、[認証関連のライブラリや実装例](https://docs.microsoft.com/ja-jp/azure/active-directory/develop/active-directory-code-samples#web-application-to-web-api)も豊富です。

EC-CUBE だからって PHP で書く必要は無くて、 DB のデータを RESTful な API で安全に操作したいだけだから、 C# で書いてしまえ！というやつです。

## 前提条件

[microsoft.com/net/core](https://microsoft.com/net/core) から .NET SDK をインストールしておきます。
上記のチュートリアルでは、 Mac や Linux の方は Visual Studio Code をおすすめされますが、 vim しか使いたくないんだよ！という方や、 PhpStorm が慣れてるから〜という方にも、[便利な開発ツール](https://www.microsoft.com/net/learn/tools) が用意されていますので安心してください。
ちなみに、僕は [Omnisharp Emacs](http://www.omnisharp.net/#integrations) を使ってます。

あ、別途 EC-CUBE をインストールしておいてくださいね。 2系でも 3系でも大丈夫です。
手っ取り早く済ませたい方は、 [1行のコマンドでローカル環境にEC-CUBEを最速でインストールする](https://qiita.com/nanasess/items/debc18697a89a7ccaddb) のがおすすめです。

## .NET Core プロジェクトの作成

まず、ソリューションを作成します。

```shell
mkdir dotnetapi
cd dotnetapi
dotnet new sln
```

続いて、 EC-CUBE API プロジェクトを作成します。

```shell
dotnet new webapi -o EccubeAPI -n EccubeAPI -f netcoreapp2.0
dotnet sln ./dotnetapi.sln add ./EccubeAPI/EccubeAPI.csproj
```

プロジェクトに必要な nuget パッケージを追加していきます

```shell
dotnet add EccubeAPI package Microsoft.AspNetCore.All --version 2.0.0
dotnet add EccubeAPI package Microsoft.AspNetCore.Server.IISIntegration --version 2.0.0
dotnet add EccubeAPI package Microsoft.AspNetCore.Server.Kestrel --version 2.0.0
dotnet add EccubeAPI package Microsoft.Extensions.Configuration.EnvironmentVariables --version 2.0.0
dotnet add EccubeAPI package Microsoft.Extensions.Configuration.FileExtensions --version 2.0.0
dotnet add EccubeAPI package Microsoft.Extensions.Configuration.Json --version 2.0.0
dotnet add EccubeAPI package Microsoft.Extensions.Logging --version 2.0.0
dotnet add EccubeAPI package Microsoft.Extensions.Logging.Console --version 2.0.0
dotnet add EccubeAPI package Microsoft.Extensions.Logging.Debug --version 2.0.0
dotnet add EccubeAPI package Microsoft.Extensions.Options.ConfigurationExtensions --version 2.0.0
dotnet add EccubeAPI package Swashbuckle --version 6.0.0-beta902
dotnet add EccubeAPI package Microsoft.EntityFrameworkCore.Design --version 2.0.0
dotnet add EccubeAPI package Microsoft.EntityFrameworkCore.Relational --version 2.0.0
```

お使いの DB に対応した EntityFrameworkCore パッケージをインストールします。

```shell
## PostgreSQL
dotnet add EccubeAPI package Npgsql.EntityFrameworkCore.PostgreSQL --version 2.0.0

## MySQL
dotnet add EccubeAPI package Pomelo.EntityFrameworkCore.MySql --version 2.0.0

## SQLite3
dotnet add EccubeAPI package Microsoft.EntityFrameworkCore.Sqlite --version 2.0.0
```

dotnet ef コマンドを有効にするため、 `EccubeAPI/EccubeAPI.csproj` ファイルを開いて、 `DotNetCliToolReference` の書かれている `<Items>` に以下を追加します。

```xml
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
```

EccubeAPI ディレクトリで dotnet restore コマンドを実行すると、 dotnet ef コマンドが使用できるようになります。

```shell
cd EccubeAPI
dotnet restore
dotnet ef --help

Entity Framework Core .NET Command Line Tools 2.0.0-rtm-26452

Usage: dotnet ef [options] [command]

Options:
  --version        Show version information
  -h|--help        Show help information
  -v|--verbose     Show verbose output.
  --no-color       Don't colorize output.
  --prefix-output  Prefix output with level.

Commands:
  database    Commands to manage the database.
  dbcontext   Commands to manage DbContext types.
  migrations  Commands to manage migrations.

Use "dotnet ef [command] --help" for more information about a command.
```

## scaffold の作成

以下のコマンドを実行します。 ConnectionString 及び Provider は、使用している DB に応じて変更します。

```shell
dotnet ef dbcontext scaffold "<ConnectionString>" "<Provider>" -o Models
```

| RDBMS      | Provider                              | ConnectionString                                                                         |
|------------+---------------------------------------+------------------------------------------------------------------------------------------|
| PostgreSQL | Npgsql.EntityFrameworkCore.PostgreSQL | "Server=127.0.0.1;Port=5432;Database=myDataBase;User Id=myUsername;Password=myPassword;" |
| MySQL      | Pomelo.EntityFrameworkCore.MySql      | "Server=myServerAddress;Database=myDataBase;Uid=myUsername;Pwd=myPassword;"              |
| SQLite3    | Microsoft.EntityFrameworkCore.Sqlite  | "Data Source=/path/to/eccube.db;"                                                        |

*[ConnectionString は、こちらを参考に](https://www.connectionstrings.com)*

このコマンドで Models 以下に、各テーブルに対応するモデルクラスが生成されます。

## 接続設定

DB とプログラムが接続できるよう設定します。

Models/eccubeContext.cs を開き、コンストラクタを追加します。

```Models/eccubeContext.cs
        /// <summary>
        ///   コンストラクタ
        /// </summary>
        public eccubeContext(DbContextOptions<eccubeContext> options) : base(options)
        {
        }

```

Startup.cs の ConfigureServices メソッドと Configure メソッド に接続設定を追記します。

```Startup.cs
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // SQLite3 の場合
            services.AddDbContext<eccubeContext>(options =>
                                                 options.UseSqlite(Configuration.GetConnectionString("eccubeContext")));

            // MySQL の場合
            services.AddDbContext<eccubeContext>(options =>
                                                 options.UseMySql(Configuration.GetConnectionString("eccubeContext")));

            // PostgreSQL の場合
            services.AddDbContext<eccubeContext>(options =>
                                                 options.UseNpgsql(Configuration.GetConnectionString("eccubeContext")));
            services.AddMvc();
            services.AddSwaggerGen(); // 追加
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc();
            app.UseSwagger();   // 追加
            app.UseSwaggerUi(); // 追加
        }
```

appsettings.json に ConnectionString を追記します。

```appsettings.json
  "ConnectionStrings": {
    "eccubeContext": "<ConnectionString>"
  }
```

## Controller の作成

商品データを取得するためのコントローラクラスを作成してみます。

```Controllers/ProductController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EccubeAPI.Models;
using Microsoft.AspNetCore.Mvc;

namespace EccubeAPI.Controllers
{
    [Route("api/[controller]")]
    public class ProductController : Controller
    {
        private eccubeContext context;

        public ProductController(eccubeContext context)
        {
            this.context = context;
        }

        // GET api/values
        [HttpGet]
        public IActionResult Get()
        {
            var products = this.context.DtbProduct.ToList();
            return new ObjectResult(products);
        }

        // GET api/values/5
        [HttpGet("{id}")]
        public async Task<IActionResult> Get(int id)
        {
            var product = await this.context.DtbProduct.FindAsync((long)id);
            return new ObjectResult(product);
        }
    }
}

```

## ビルドと実行

以下のコマンドでローカル環境に Webサーバーである Kestrel が起動します。

```shell
dotnet build
dotnet run
```

http://localhost:5000/swagger/ui/index.html へアクセスすると、 Swagger UI が表示され、 先ほど作成した API のテストが可能です！

## API のテスト

さっそく curl コマンドで API にアクセスしてみましょう！

```
curl -X GET --header 'Accept: application/json' 'http://localhost:5000/api/Product/1'
```

```json
{
  "productId": 1,
  "creatorId": 1,
  "status": 1,
  "name": "ディナーフォーク",
  "note": null,
  "descriptionList": null,
  "descriptionDetail": "セットで揃えたいディナー用のカトラリー。\n定番の銀製は、シルバー特有の美しい輝きと柔らかな曲線が特徴です。適度な重みと日本人の手に合いやすいサイズ感で長く愛用いただけます。\n最高級プラチナフォークは、贈り物としても人気です。",
  "searchWord": null,
  "freeArea": null,
  "delFlg": 0,
  "createDate": "2017-12-10 08:28:05",
  "updateDate": "2017-12-10 08:28:05",
  "creator": null,
  "statusNavigation": null,
  "dtbCustomerFavoriteProduct": [],
  "dtbOrderDetail": [],
  "dtbProductCategory": [],
  "dtbProductClass": [],
  "dtbProductImage": [],
  "dtbProductTag": [],
  "dtbShipmentItem": [],
  "dtbTaxRule": []
}
```

## まとめ

.NET Core を使用することで、大変少ないコード量で API を作成することができました。
古い EC-CUBE でも、 .NET Core の動作する環境からデータベースにさえ接続できれば動作させることが可能です。
また、 PHP に比べ、[大変パフォーマンスが良い](http://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=csharpcore&lang2=php)のも特徴です。

いつもは PHP で頑張ってる EC-CUBE プログラマーな方にも、ぜひチャレンジしていただきたいです。

## こちらも参考に

- [Azure x EC-CUBE + WordPress + SQL Database で最強の EC CMS を構築しよう](https://qiita.com/nanasess/items/5f7ff1a3f030f85032cf)
- [Visual Studio Code でprecompiled なAzure Functions を作る。](https://qiita.com/sato_ryu/items/818b6d172fbd501fe430)
- [Zalando RESTful API と イベントスキーマのガイドライン](https://restful-api-guidelines-ja.netlify.com)