【CGI】Vue.js + axiosでサーバのPythonプログラムを動かし出力データをもらう

# はじめに
フロントエンドで全部できるっちゃできるけど、やっぱり計算部分にPythonの楽さは欲しいなあ、という思いからサーバでプログラムを動的に動かすCGIを触ってみたのでそのまとめ。バックエンドは未だ疎い。

フリーレンタルサーバーのXREAのアカウントを持っているので実際にそこで試してみる。
[無料から使える高機能・高品質レンタルサーバー | XREA（エクスリア）](https://www.xrea.com/)

やりたいことは、

1. ボタンをクリック
2. Vue.jsとaxiosを使ってデータをサーバに渡す
3. Pythonで書かれたCGIスクリプトを実行して演算、結果をJSONで返す
4. 同じ画面に返されたデータを表示

というかんじ。

今回は例として入力された2数の最大公約数と最小公倍数を計算して返すプログラムを作る。


# 作ってみる

## ファイルの配置
```
├─ cgi-bin
│    └─ index.cgi
├─ index.html
└─ main.js
```
Pythonで書かれたCGIスクリプトは `cgi-bin` というディレクトリを作ってその中に投入。Pythonで書かれてるけど拡張子は `.cgi` 。

サーバにアップロードした後、CGIまわりのパーミッションを指定する必要があるけどそれは後述。

## HTML (index.html)

```html
<html>
<head>
    <meta charset="UTF-8">
    <title>最大公約数と最小公倍数を返す</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
    <script type="module" src="main.js"></script>
</head>
<body>
    <div id="app">
        <!-- 入力部分 -->
        <p>
            <input v-model="a" type="number" value="12">
            <input v-model="b" type="number" value="15">
            <button v-on:click="getResult">計算</button>
        </p>
        <!-- 表示部分 -->
        <p>
            最大公約数: {{ result['gcd'] }}<br>
            最小公倍数: {{ result['lcm'] }}
        </p>
    </div>
</body>
</html>
```

CDNでVue.jsとaxiosを読み込んでおく。
Vueで書いたJavaScriptのファイルは `type="module"` で読み込むと、DOMが読み込まれた後に実行されるので安心。

## JavaScript (main.js)

```js
var vm = new Vue({
    el: '#app',
    data: {
        a: 12,
        b: 15,
        result: {},  // 返ってきたオブジェクトを格納する変数
    },
    methods: {
        getResult: function() {
            const url = './cgi-bin/';
            // axiosでデータの受け渡し
            axios.get(url, {
                params: {  // paramsでパラメータを指定
                    a: this.a,
                    b: this.b
                }
            })
            .then(res => this.result = res.data)  // データをresultに格納
        }
    }
});
```

Vueインスタンスを作ってaxiosでデータの受け渡しをする。
URLは `./cgi-bin/` 。 `index.cgi` という名前ならHTML同様ファイル名を指定しなくてもOKなもよう。

また、返ってくるデータ `res.data` はオブジェクトとして取り出せるので `JSON.parse()` とかで直す必要がなかった。（JSONにして返したんだからJSON文字列で返ってくるだろうという思い込みにやられた。）
ちなみにやるとこんなエラーが出る。

```
SyntaxError: Unexpected token o in JSON at position 1
```
`o` ってなんぞやと思ったら `object` の `o` らしい...

## Python CGI (index.cgi)
```py
#!/usr/local/bin/python3
# -*- coding: UTF-8 -*-
import cgi
import cgitb
import math
import json

cgitb.enable()  # CGIのデバッグをオン

form = cgi.FieldStorage()  # GETで得たデータを格納
a = int(form['a'].value)   # データは文字列でやってくるので数値に変換
b = int(form['b'].value)

data = {
    'gcd': math.gcd(a, b),  # math.gcd() は Python 3.5 で追加されたらしい
    'lcm': a * b // math.gcd(a, b)
}

print('Content-Type: application/json')
print()
print(json.dumps(data))  # json形式にして返す
```

行頭でPythonのインタプリタの場所を指定。レンタルサーバの仕様ページに各言語のCGIの実行パスが書いてあると思うので確認する。XREAだとここ。
![xrea_py_path.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415493/ff3ce161-bc07-2375-9cee-40ef814a4e18.png)

`Context-Type` 指定の行の後の `print()` は改行のためで、これを入れないとダメな仕様みたい。 普通に `Context-Type` 文字列の最後に `\n` してもOK。

アップロード後はCGIスクリプトを入れたディレクトリとそのファイルのパーミッションを指定する必要があり、XREAにも推奨パーミッションが書いてある。
![xrea_permission.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415493/76dd1b27-7740-baf0-a59b-7570a92336c4.png)
特に、CGIの実行ファイルは700以上（実行可能）にする必要があるのは注意（よく忘れた）。

以上のXREAの仕様が書いてあるリンクも貼っておく。
[仕様 | 無料から使える高機能・高品質レンタルサーバー | XREA（エクスリア）](https://www.xrea.com/spec/)

# 実行
ファイルを上げて実際に試してみた画面がこちら
![xrea_cgi_disp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415493/fea60176-8359-acee-f584-931e397e15a3.png)
いいかんじ。空欄や小数で送信したら普通にエラーになるけど、まあお試しなので。

以上。