[Flask]簡易ログイン機能+多言語JSONのDB[RESTful]

#簡易ログイン機能+多言語JSONのDBを作る![a.jpg](https://qiita-image-store.s3.amazonaws.com/0/251684/a08a0052-e62c-2a28-9d70-300c9482d2b7.jpeg)
新年あけましておめでとうございます。この記事では、旧年の成果を発表いたしたいと思います。筆者のTwitter: yosiyos38795255 GitHub: @yosiyoshi です。よろしくお願いいたします。
##(1)必要な環境
まず、Python3の開発環境(筆者の場合はAnaconda3+Spyder)が必要。


次にpipでWebフレームワークのFlaskをインストール：
`pip install Flask`
あとはcurlなどがインストールされているならば、それを利用すること。

##(2)目標
RESTfulなJSONデータベースを利用して簡単なログインシステムと、その認証後に開始されるデータベースのバックエンドコードを実装すること。ただし、実はデータベース本体にログイン機能は付属しないので、ログインを通さずに操作できます（要するにログインもどき）。

##(3)実装
https://github.com/yosiyoshi/flask_practice


まず、ログイン機能の実装は以下のライブラリを使用します。

```python
from flask import Flask, render_template, jsonify, request
import json
import re
import subprocess
import webbrowser
```

ユーザデータはcurlによりJSON形式で予め登録されるものとします。
まず、JSON-APIのI/Oを司るバックエンドコードをlogin.pyとして、まずその基礎部分を以下のように作成します：

```python
app = Flask(__name__)

data = []

@app.route('/')
def form():
   return render_template('login.html')

@app.route('/data', methods=['POST'])
def add_data():
  data.append(request.get_json())
  return '', 204

#ここにログイン処理を書く

if __name__ == "__main__":
    app.run(host='localhost', debug=True)
```

@app.route('/')とは、
`$ python login.py`
実行時にアクセスできるローカルアドレス"localhost(:5000)"で行なわれる処理の定義を宣言するものです。次の行にある、`def form():`以降の`render_template('login.html')`が、実行されるlogin.pyのディレクトリ直下に存在する"templates/login.html"を呼び出し、これをテンプレートとして読み込むのです。

@app.route('/data', methods=['POST'])とは、"localhost:5000/data"へのJSONデータの書き込みを許可するもので、コード実行中に当該アドレスへのcurl使用が可能です。

`curl -i -H "Content-Type: application/json" -d "{\"id\":\"a\",\"password\": \"a\"}" http://localhost:5000/data`

例えば、以上のコードを実行すると、"localhost:5000/data"に存在するデータベースへ、

`{"id":"a","password": "a"}`

というJSONデータが書き込まれます。ただし後述するログイン処理を考慮に入れると、ログインするのに必要なJSONデータは一つ（つまり一人分のidとpasswordの組合せ）だけです。なお、当該アドレス"localhost:5000/data"へのブラウザによるアクセスは許可されません。したがって、外部からのデータ読み込みはできません。

次に、ログイン処理を書きます：

```python
@app.route('/auth', methods = ['POST', 'GET'])
def result():
   if request.method == 'POST':
      getkey = request.form['key']
      getvalue = request.form['value']
      authkey = "id:"+getkey+",password:"+getvalue
      jsdump = json.dumps(data)
      key = re.sub(r'["{[/ \]}"]', "", jsdump)
      if key == authkey:
         auth_res = "Login successful"
　　　　　#ログイン後にサブプロセスを起動したいので、後にここへ追記
      else:
         auth_res = "Login failed"
      return "Result: {}".format(auth_res)
```
このgetkeyとgetvalueで、idとpasswordのテキストフォームに入力された文字列を呼び出し、authkeyという新しい文字列に結合します。jsdumpでは、先ほど入力された
`{"id":"a","password": "a"}`
のようなJSONデータをstringデータに変換し、
keyで{}や[]のようなJSONデータ内に元々含まれていた記号を、re.sub(文字列,"")の命令により空文字へ置き換え、削除します。

そして、key(登録されていたJSONのidとパスワード) == authkey(入力されたidとパスワード)が一致した場合にauth_res(ログイン結果)として"Login successful"、あるいはそうでなければ"Login failed"を返します。

![b.jpg](https://qiita-image-store.s3.amazonaws.com/0/251684/caa13cc0-6173-34b5-1934-39d51e08bad4.jpeg)
ログインフォーム

![c.jpg](https://qiita-image-store.s3.amazonaws.com/0/251684/bb6884dc-3bf2-ec15-fea5-6d5f75de7e6c.jpeg)
ログインが成功すると、この表示が出ます。

ここまでのコード： https://github.com/yosiyoshi/flask_practice/blob/master/login.py

`/templates`ディレクトリにlogin.htmlを作成：

```html
<html>
   <body>
      <form action = "/auth" method = "POST">
         <p>Name /ID: <input type = "text" name = "key" /></p>
         <p>Password: <input type ="text" name = "value" /></p>
         <p><input type = "submit"  value = "submit" /></p>
      </form>
   </body>
</html>
```

ここまでのコード： https://github.com/yosiyoshi/flask_practice/blob/master/templates/login.html

さて、ログインが成功したのちの処理を"lang_json.py"として次のように記述：

```python
from flask import Flask, jsonify, request
from ynltk import Langvowel
from collections import Counter
import json

app = Flask(__name__)
l = Langvowel()

data = []

@app.route('/')
def info():
  return "'/data' to show the content; '/lang' to detect in what language written; '/len' to measure the length."

@app.route('/auth')
def message():
   return "Login successful: please visit http://localhost:5000/ again."

@app.route('/data')
def get_data():
  return jsonify(data)

@app.route('/lang')
def detec_lang():
  return l.langvowel(str(data))

@app.route('/len')
def json_len():
  return str(len(json.dumps(data)))

@app.route('/data', methods=['POST'])
def add_data():
  data.append(request.get_json())
  return '', 204

if __name__ == '__main__':
    app.run(host='localhost',debug=True)
    print("Index information is here -> http://localhost:5000/")
```

ynltkは私がhttps://github.com/yosiyoshi/IntelligenTXT/blob/master/ynltk.py
にて公開している自然言語処理のライブラリで、今回は言語判定に利用します。

まずこのコードを実行すると、curlを通じてJSONデータのI/Oと"localhost:5000/data"のデータへアクセスすることができます。

![rest2.png](https://qiita-image-store.s3.amazonaws.com/0/251684/756bc6ea-2764-ae89-d880-fd2c60b59035.png)

そして、"/data"では登録されたデータを一覧にしてみることができます。

![rest3.png](https://qiita-image-store.s3.amazonaws.com/0/251684/9650e3bd-1239-1d8b-f6db-071998e9b5cd.png)

"/len"ではJSONデータの「長さ（文字数？）」が計算され、返されます。
![rest4.png](https://qiita-image-store.s3.amazonaws.com/0/251684/c0abf970-cd93-6cf2-027b-8a50305a39ca.png)

"/lang"では入力されたJSONデータの言語が判別され、返されます。
![rest5.png](https://qiita-image-store.s3.amazonaws.com/0/251684/ae2bf806-75aa-9168-c33a-ced3d1ae6092.png)

ここまでのコード： https://github.com/yosiyoshi/flask_practice/blob/master/lang_json.py

先に`login.py（簡易ログイン）`と`lang_json.py（JSONデータベース）`という二つのコードを独立して書きましたが、ここから先は二つの機能を連携させる必要があります。つまり、ログイン→データベース操作というフローを完成させるのです。

そのため、`login.py`を小改造し、`log2json.py`に書き換えます：

```python
#@app.route('/auth', methods = ['POST', 'GET'])
#def result():
#   if request.method == 'POST':
#      getkey = request.form['key']
#      getvalue = request.form['value']
#      authkey = "id:"+getkey+",password:"+getvalue
#      jsdump = json.dumps(data)
#      key = re.sub(r'["{[/ \]}"]', "", jsdump)
#      if key == authkey:
#         auth_res = "Login successful"
         subprocess.call(['python', 'lang_json.py'])
         url = "http://localhost:5000/"
         webbrowser.open(url)
#      else:
#         auth_res = "Login failed"
#      return "Result: {}".format(auth_res)
```
コメントアウトされていない部分のコードを、"localhost:5000/auth"の挙動に追加することで、"Login successful"が表示される前に、先述の'lang_json.py'が起動します。

この時ログインが成功すると、

![c.jpg](https://qiita-image-store.s3.amazonaws.com/0/251684/5b3637c7-85b3-2ee0-1201-f4ecf65a5109.jpeg)
を表示することなく、次のような画面が表示されます：

![d.jpg](https://qiita-image-store.s3.amazonaws.com/0/251684/87338eba-53c0-eb48-f467-d12aceb4f0a5.jpeg)

こうなれば成功です。
ここまでのコード： https://github.com/yosiyoshi/flask_practice/blob/master/log2json.py

##(3)まとめ
![a.jpg](https://qiita-image-store.s3.amazonaws.com/0/251684/fad8b929-acba-30e5-6531-be7b09467269.jpeg)
まずログインすると、データベースに誘導されます。
![d.jpg](https://qiita-image-store.s3.amazonaws.com/0/251684/ed946cd1-7592-0b27-030d-90a3d9107d7e.jpeg)
データベースはRESTfulスタイルで、外部から編集できます。
![result.png](https://qiita-image-store.s3.amazonaws.com/0/251684/83daecf9-317a-38a4-c079-f590d21aaf28.png)

##(4)最後に
駄文長文失礼しました。もともとこれらのコードは独立しており、Flaskの演習として弄っていました。従って注意が及ばず、言葉足らずや知識不足の部分も残っていると思います。ご指摘やご教授は歓迎いたします。ご閲覧ありがとうございました。