WEB初心者がAWS上でDjango + WSGI + ApacheでREST APIを作るまで

## 背景
大学院の授業で突如REST APIを作る必要に迫られました。
まる2日間ほどかかって大変でした。
せっかくがんばったので備忘録のため、また同じような目的の方のために記事を書きました。

## 0 目的
http://~~~/?key=hogeというふうにリクエストしたら、json形式のデータを返してくれるAPIを作ること

使うツールはDjango, WSGI, Apache

Flaskなんてものは知りません。

## 1 環境
  Amazon Linux AMI (t2.micro)
  python 2.7
  Django 1.11
  Apache 2.2

## 2 方法

### 2.0 AWS
することリスト

* EC2インスタンスの立ち上げ
* セキュリティグループにポート番号80を追加（**重要**）
* Elastic IP アドレスをEC2インスタンスに割り当てる

詳細は省きます。

以降の作業は全てサーバー上で行います。

### 2.1 Django（ジャンゴ）
まずDjangoから準備していきます。
必要に応じてsudoつけてください。

```
$ pip install django==1.11
$ pip install djangorestframework
$ cd 任意の場所
$ django-admin startproject mysite
$ cd mysite
$ python manage.py startapp myapp
$ cd myapp
$ vi views.py
```

次にmysite/myapp/views.pyを編集していきます。
ここがREST APIで受け取った内容をもとに返す内容を作る重要な部分です。
例えば以下のようにします。

```python:mysite/myapp/views.py
from django.http import JsonResponse
from rest_framework.views import APIView

class View(APIView):
    def get(self, request):
        if 'key' in request.GET:
            key = request.GET.get('key')
            # 本来はここでkeyをもとにoutputをつくる
            out={u'key':key}
        else:
            out = {u'key':u'Please specify a key query'}
        return JsonResponse(out, status=201)
```

今回は与えられたkeyをそのままjson形式にして返す単純なAPIにしました。

さりげなくrest_frameworkをimportしていますが
これは先ほどpip installした[DjangoRESTframework](http://www.django-rest-framework.org)のことで、
DjangoでREST APIを作るのに欠かせないパッケージとなっています。

入力と出力が定義できたところで、これまた重要なURI(http://...みたいなやつ)の設定をします。
以下の2つのファイルを編集・作成します。

* mysite/mysite/urls.py
* mysite/myapp/urls.py

```python:mysite/mysite/urls.py
from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^', include('myapp.urls')),
]
```

```python:mysite/myapp/urls.py
from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^myapi/$', views.View.as_view()),
]
```

細かい説明は省きますが、いま編集・作成した3つのファイルによって特定のURIと特定の機能を結びつけたことになります。
Djangoにはデータベースに簡単にアクセスできるmapperもあるので便利です。
リクエストを捌きながらデータベースに出し入れしたい方は公式ドキュメントを参考にお願いします。

また、mysite/myapp/settings.pyも編集する必要があります。

```python:mysite/myapp/settings.py
- ALLOWED_HOSTS = []
+ ALLOWED_HOSTS = [ec2インスタンスのパブリックDNS]
```
上記のように、初期設定では[]となっているALLOWED_HOSTSリストの要素として
自分のec2インスタンスのパブリックDNSを文字列として足します。

要は"ec2-***.com"みたいなやつをリストに入れます。

"***.com"みたいな独自のHostを持っているならそれで。

この設定を忘れるとのちのちアクセス時にInternalServerErrorなるものが吐かれます。

### 2.2 Apache（アパッチ）
httpd -versionと打つとApacheのバージョンが確認できるので、念のため確認します。

```
$ httpd -version
Server version: Apache/2.2.34 (Unix)
Server built:   Nov  1 2017 18:47:16
```
このようにデフォルトでは Apache 2.2 が入っているはずです。

Apache 2.2 と Apache 2.4 では設定ファイルの記法に一部異なる部分があり、注意が必要です。詳しくは第2章3節で。

httpdですが、今回は主に以下の3つを使います

```
$ sudo service httpd start
$ sudo service httpd stop
$ sudo service httpd restart
```

全て読んで字のごとく、サーバーのスタート、ストップ、再起動です。

### 2.3 WSGI(ウィズギー)

Django、Apacheの次はWSGI(Web Server Gateway Interface)です。
WSGIとはWEBサーバーとWEBアプリケーション(フレームワーク)を接続するためのインターフェース定義だそうです。(!?)
今回はPythonのために用意されたWSGIであるmod_wsgiを使います。
これによってDjangoとApacheを橋渡しします。
少しでも設定を誤るとうまく動作しないので、ある意味これも重要です。

```
$ sudo yum install httpd-devel # これがないとmod_wsgiをpip installできない
$ sudo pip install mod_wsgi
```

httpd-develがない状態でmod_wsgiをpip installしようとすると、apxsがないと怒られます。

次にApacheの設定ファイルにmod_wsgiのことを書きます。
任意の場所に以下のコードを継ぎ足して保存します。

```:/etc/httpd/conf/httpd.conf 
LoadModule wsgi_module /usr/local/lib64/python2.7/site-packages/mod_wsgi/server/mod_wsgi-py27.so
WSGIScriptAlias / /path/to/mysite/mysite/wsgi.py
WSGIPythonPath /path/to/mysite

<Directory /path/to/im2txt/im2txt>
<Files wsgi.py>
Order deny,allow
Allow from all
</Files>
</Directory>
```

1行目は、wsgiの場所をApacheに伝えるため
2行目は、外部からルートURL(/)にアクセスされたら/path/to/im2txt/im2txt/wsgi.pyを呼ぶ、という意味
3行目は、mysiteをpythonpathに加えている
4行目以降は、外部からwsgi.pyへのアクセスを許可するためのものです

さて、このhttpd.confなのですが、Apache 2.4では`Allow from all`ではなく`Require all granted`としなければなりません。
先ほどApacheのバージョンを確認した理由はこれです。

また/path/to/やmod_wsgi-py27.soの場所はそれぞれの環境に合わせて変更してください。
pyenvを使っている場合は、pyenvのsite-packagesからmod_wsgi-*.soを引っ張ってくる必要があるそうです。
なんにせよ環境に合わせて適宜変えてください。

また、/path/to/wsgi.pyをその他ユーザーが探せるように、パーミッションを適切に変更する必要があります。しないと後で403 Forbiddenというエラーがでます。詳しくは第４章で。

## 3 実行

```
$ sudo service httpd start
```

でApacheを起動後、ブラウザからhttp:://[パブリックDNS]/myapi/?key=aaaにアクセスしてみてください。

httpd.confはApacheが起動するときに読まれるらしいので、httpd.confの変更を反映させたいときは、restartしましょう。

問題なくできていればjson形式のデータが表示されるはずです。

何一つトラブルなく成功できた方はぜひ「いいねボタン」を押していただけると幸いです。
励みになります。

## 4 エラー

エラーがでたら、

* エラーメッセージを読む
* sudo tail /var/log/httpd/error_logでエラーログを見る
* わからなかったらエラーでぐぐる

で大体解決できます。

おそらく初心者にありがちなのが、403 Forbidden You don't have permission to access / on this server. というエラー。
僕が初心者ゆえに一番てこずったエラーです。
最初は403の意味も知らず、エラーログを見る習慣もなく、途方にくれていました・・・。

原因は、WSGIScriptAliasでひも付けられたwsgi.pyへいたるディレクトリまたはwsgi.pyのパーミッションが適切でなかったからでした。
[Linuxの権限確認と変更（超初心者向け）
](https://qiita.com/shisama/items/5f4c4fa768642aad9e06)が初心者にはとてもわかりやすかったです。
この辺りの知識は持ってて当然みたいな空気があるのか、サーバー構築系のブログにパーミッション変更の記述がありません。ガチ勢コワイヨ

要はルートディレクトリ(/)からwsgi.pyへいたるまでのすべてのディレクトリにその他ユーザー(o)に対して実行の権限(x)を与えて(+)、wsgi.pyにその他ユーザー(o)に対して読み取り権限(r)が与えられていることをls -lやls -ldを駆使して確認すればOKです。

## お疲れ様でした

極力僕のようなWEB初心者でもひっかからないように最初から最後まで書きました。
何か間違った記述などあればコメントしていただけると幸いです。
何しろWEB初心者なので間違った言葉遣いをしている気がしてならないです。
あと今回使わなかったFlaskの利点とか教えて欲しいです。