はじめての Flask #5 ~JSONを返すWebAPIを書こう~

[前回: はじめての Flask #4 ~データベースをSQLAlchemyでいじってみよう~](https://qiita.com/nagataaaas/items/5c7c9ec4813fea85c40c)

今までの学習を通して、様々なwebアプリが作れるようになったと思います。

さて、今回はいよいよJSONを返すWebAPIを作ろうと思います！

# 何がうれしいの
WebAPIがあると何がうれしいの？ということですが、大まかに以下の理由があげられます。

・内部処理とビュー(外面)を分けることができる
・さまざまなプラットフォームでの利用に簡単に対応することができる
・細かく処理を分けることができるので、設計自体が非常にわかりやすく、かつ組み立てやすくなる

上記の3つが主に考えられるものです。
それぞれわかりやすく説明してみましょう。

## 内部処理とビュー(外面)を分けることができる

今までの内容で作ったwebサービスであれば、1つのページに対して1つの動作というペアが完成していました。あるページからほかのページの内容をそのまま取得することはできず、情報の取得はページの遷移でしか行えないものでした。つまり、情報の内容とUI(HTMLなど)は切り離せない状態で返すしかなかったのです。
しかし、JSONという**情報のみを扱うデータ**を返すことができるようになると、その情報の表現はUI次第になります。UIが古臭いならば見てくれのみ改修すればよく、セキュリティに問題があるならばWebAPIのみ変更すればよいのです。
この分離が、WebAPI最大の特徴ともいえます。

## さまざまなプラットフォームでの利用に簡単に対応することができる

上記の通り、情報とUIの分離に成功すれば、UIのみそれぞれのに対応するように設計すれば、WebAPI自体はノータッチで再利用できます。
ブラウザ版も、スマホアプリ版も、どのような利用方法であったとしても内部処理は変わらないので、バックエンド開発側もフロントエンド開発側も楽に実装ができます。

## 細かく処理を分けることができるので、設計自体が非常にわかりやすく、かつ組み立てやすくなる

あたかもプログラム内で関数を定義するかのように、目的の動作を行うエンドポイントを作成すればその組み合わせで容易に処理が実行できます。
目的の異なるエンドポイントを複数作成する方が、互いに複雑に絡み合った長大な単一のエンドポイントを作成・保守するよりも容易なことは想像に難くないはずです。

# 早速作ろう！

ということで、まず大切な**JSONの返し方**から学んでいきましょう！

## JSONの返し方
```python
from flask import *

app = Flask(__name__)


@app.route("/")
def index():
    return jsonify({"language": "python"})


if __name__ == "__main__":
    app.run(port=8888)
```

このように`flask.jsonify`にdict型を与えるとそれをJSONにダンプしてくれますので、それを返しましょう。ちなみに、このjsonifyを行ったときは`header`の`Content-Type`も`application/json`にしてくれます。ありがとうFlask。

このページに接続すると

```json
{"language": "python"}
```
というJSONを返します。

ちなみに

```python
from flask import *
import json

app = Flask(__name__)


@app.route("/")
def index():
    return json.dumps({"language": "python"})


if __name__ == "__main__":
    app.run(port=8888)
```

のように自ら`json.dumps`を使ってJSONをダンプすることもできますが、こちらは`header`の`Content-Type`が`text/html; charset=utf-8`となり、みずから`header`を書き換える必要が出てくるので、`flask.jsonify`を使う方が良いです。

## ステータスコードも一緒に返そう！
WebAPIを作ると「認証が間違っている!!(400番)」、「お前ちょっとリクエスト送りすぎ!!(429番)」、「ごめん、エラー起きた(500番)」など、200番以外のステータスコードを返したくなることもあります(というか返しましょう)。
そのときには、エンドポイント内の`return`で、ステータスコードの番号も一緒に返しましょう！

```python
from flask import *

app = Flask(__name__)


@app.route("/")
def index():
    return jsonify({"language": "python"}), 418


if __name__ == "__main__":
    app.run(port=8888)
```

このようにすることで、今回であれば「I'M A TEAPOT」(418番)のステータスコードを返すことができます。

## 日本語を返したい！

ちなみに、flaskの`flask.jsonify`では、すべての文字はデフォルトでは`ascii`となります。ですので、日本語を返そうと以下のようなコードを書くと、不都合が起きます。

```python
from flask import *

app = Flask(__name__)


@app.route("/")
def index():
    return jsonify({"language": "パイソン"}), 418


if __name__ == "__main__":
    app.run(port=8888)
```

帰ってくるJSONは

```JSON
{"language":"\u30d1\u30a4\u30bd\u30f3"}
```

となります。わぁ、Unicodeだぁ…。
はい、ですので、文字コードの設定を`UTF-8(asciiじゃなくする)`にしてあげましょう。

```python
from flask import *

app = Flask(__name__)
app.config["JSON_AS_ASCII"] = False

@app.route("/")
def index():
    return jsonify({"language": "パイソン"}), 418


if __name__ == "__main__":
    app.run(port=8888)
```

このように、`app.config["JSON_AS_ASCII"]`を`False`にすることで日本語・漢字・そのほか多くの文字をJSONに正しくダンプできます。

これまでの情報と、`GET`, `POST`(ほかにも`PUT`などのメソッドもありますが、それは他の記事に譲ります)のやり取りを組み合わせることで、WebAPIは作成できます。

それではみなさん、良いPython Lifeを！！