Bottle、Flaskを使ってWebサーバを起動する(Apacheも使ってやってみた)

# はじめに
PythonでWebサーバを起動する方法を学んだので、アウトプットする目的で投稿します。
どなたかの参考になれば、幸いです。

本稿では標準ライブラリである`http`モジュールや`urllib`モジュールは取り上げず、`bottle`、`Flask`を使用したサンプルコードを元に説明します。
また、`Apache`とPythonスクリプトを接続する方法についても説明します。

# 環境
* Windows 10 home
* Python 3.7.4

# WSGI
PythonでWebの勉強をしているとWSGIという言葉がよく頻出します。

WSGIはPythonのWebアプリケーションとWebサーバーの間の標準化されたAPIのことを指します。

PythonのWebフレームワーク、Webサーバの多くはWSGIを使っています。
フレームワークやサーバの開発者以外がWSGI自体の仕組みを知る必要性は高くないことが言われています。

これから取り上げるフレームワークはすべてWSGIを使用しています。

# Bottle

Bottleは1つのPythonファイルだけから作られているので、非常に試しやすく、デプロイが容易です。

次のコードでWebサーバを実行し、 `http://localhost:9999` にGETメソッドでアクセスしたとき、レスポンスを返します。

前準備としてBottleをインストールしましょう。

``` console
$ pip install bottle
```

```bottle1.py
from bottle import route, run

@route('/')
def myhome():

    return "It's my home page"

run(host='localhost', port=9999)
``` 


```console
$ python bottle1.py
Bottle v0.12.18 server starting up (using WSGIRefServer())...
Listening on http://localhost:9999/
Hit Ctrl-C to quit.
```

`http://localhost:9999` にアクセスします。
ステータスコード200で、ボディに `It's my home page` と表示されており、レスポンスの内容がわかります。
<img width="898" alt="bottle1-api.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186620/82ca16f3-5e33-dc24-b6e1-5754e37b0e37.png">

サーバ側では、以下のように出力されています。

``` console

127.0.0.1 - - [17/Dec/2019 08:30:39] "GET / HTTP/1.1" 200 17
```

### コードについて
BottleはURLと直後の関数を対応付けるために `@route` デコレータを使用します。
この場合、 `/` を `myhome()` 関数 が処理するようにしています。今回はGETメソッドでリクエストを受け取りましたが、
`@route(method='POST')` と指定すれば、POSTメソッドによるリクエストを受け取ることができます。

`run()` 関数により、ウェブサーバを実行します。
このように簡単実装できるためテスト用として役に立つと思われます。

上記のコードでは、コード内でテキストを作成していましたが、
HTMLファイルを作成してそのファイルをブラウザで表示することもできます。

## HTMLファイルの埋め込み
まず、bottle2.pyとindex.htmlを同じフォルダ内に用意します。

index.htmlを次のように記述します。

```index.html
It's my <b>new</b> home page

``` 

bottle2.pyを次のように記述します。

```bottle2.py

from bottle import route, run, static_file

@route('/')
def main():
    return static_file('index.html', root='.')

run(host='localhost', port=9999)

```

それではサーバーを立ち上げます。

```console
$ python bottle2.py
```

`http://localhost:9999`にブラウザでアクセスします。
<img width="801" alt="bottle2-browser.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186620/85ccca8b-5ce2-930d-e5f6-24607ee036f1.png">

### コードについて
`static_file()` 関数を呼び出すことで、引数の `root` が示すカレントディレクトリの `index.html` をレスポンスとして返すことができます。

他にも様々な機能があります。
例えば、
`run()` の次の引数を追加することもできます。

* `debug=True` : HTTPエラーが返されたときにデバックページが作られます。(本場稼働するウェブサーバでは設定してはいけません。)
* `reloader=True` : Pythonコードに変更を加えたときにブラウザにページが再ロードされる。

また、URLに引数を渡して、渡された値を使うこともできます。以下のコードで実現できます。

``` python
@route('/<thing>')
def main(thing):
    return f'{thing}'
```

開発者サイト:https://bottlepy.org/docs/dev/

# Flask

FlaskもBottleと同じように簡単に使用できます。

まずはインストール。

``` console
pip install flask
```

では、flask1.pyを用意します。
要件として、

* URL「 `/` 」にアクセスするとindex.htmlファイルがマッピングされるようにしましょう。
* URL「 `/sample/some` 」にアクセスすると、「some」を引数の値としてブラウザに「some」と表示しましょう。
* URL「 `/sample1?thing=hello&something=world` 」にアクセスすると、クエリ文字列で指定した値を `flask1.html` に埋め込んで表示しましょう

ここで、Flaskの静的ファイルのデフォルトディレクトリは `static` であるため、そのディレクトリのファイルに対するURLも `/static` で始まります.

``` flask1.py
from flask import Flask, render_template, request

app = Flask(__name__, static_folder='.', static_url_path='')

# http://localhost:9999/でアクセスされたときに返す内容
@app.route('/')
def myhome():

    return app.send_static_file('index.html')

@app.route('/sample/<thing>')
def echo(thing):

    return thing

# sample1/以降のクエリ文字列を複数の引数として受け取る
@app.route('/sample1/')
def echo1():

    kwargs = {}
    kwargs['thing'] = request.args.get('thing')
    kwargs['something'] = request.args.get('something')
    return render_template('flask1.html', **kwargs)

app.run(host='localhost', port=9999, debug=True)

``` 

次にflack1.htmlを作成します。
新しく `templates` というフォルダを作成します。その配下に以下のコードを保存します。

```flask1.html
<html>

<body>
    It's my <b>new</b> home page
    <br>{{ thing }}
    <br>{{ something }}
</body>

</html>
```
サーバーを起動した後、ブラウザで`http://localhost:9999/sample1/$thing=hello&something=world`にアクセスすると以下のように表示されます。
<img width="904" alt="flask1-kwargs.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186620/f11b55eb-c9e8-ca2d-b6f5-cb2ff7101c61.png">



# Apache
次にApache HTTP Serverを使用して、Pythonのスクリプトを埋め込みましょう。

apacheウェブサーバーで特に優れているWSGIモジュールは `mod_wsgi` らしいのでこれを使用します。(引用: 入門Python3)

>参考[mod_wsgi公式ドキュメント](https://modwsgi.readthedocs.io/en/develop/index.html)


このモジュールはPythonコードをApacheプロセスの中で実行することも、Apacheと通信する別プロセスで実行することもできます。

使用OSがWindows以外であれば、すでにApache入っていますが、Windowsなら[ダウンロード](http://httpd.apache.org/docs/current/platform/windows.html)しなければなりません。
私は[Apache Lounge](http://httpd.apache.org/docs/current/platform/windows.html#page-header)から`httpd-2.4.41-win64-VS16.zips`をダウンロードしました。
ダウンロードしたファイルにApache24があるのでそれを`c:`の直下におきましょう(必須ではない)


`mod_wsgi`を使った方法は2種類あります。
詳しくは https://pypi.org/project/mod-wsgi/

今回は`pip`を使用して`mod_wsgi`をインストールします。

```console
$ pip install mod_wsgi
```


次にPythonスクリプトを記述します。
適当な場所に以下のように`wsgi.py`に記述します。(ちなみに場所は`c:/var/www/test/wsgi.py`)

```wsgi.py

import bottle

application = bottle.default_app()


@bottle.route('/')
def home():
    return "apache ok"
```

次に、`c:\Apache24\bin`に移動して
`mod_wsgi-express module-config`とコマンドを入力します。これによりモジュールの場所を確認できます。この情報は後述する`Apache`の設定に必要です。

```console
c:\Apache24\bin>mod_wsgi-express module-config                                                   
LoadFile "c:/users/appdata/local/programs/python/python37/python37.dll"                     
LoadModule wsgi_module "c:/users/appdata/local/programs/python/python37/lib/site-packages/mod_wsgi/server/mod_wsgi.cp37-win_amd64.pyd"                                                        
WSGIPythonHome "c:/users/appdata/local/programs/python/python37"
```

次に`C:\Apache24\conf\httpd.conf`のファイル末尾に以下を追記します。

```httpd.conf
LoadModule wsgi_module "c:/users/appdata/local/programs/python/python37/lib/site-packages/mod_wsgi/server/mod_wsgi.cp37-win_amd64.pyd"
WSGIPythonHome "c:/users/appdata/local/programs/python/python37"

<VirtualHost *:80>
    WSGIScriptAlias / c:/var/www/test/wsgi.py
    <Directory "c:/var/www/test">
            Require all granted      
    </Directory>
</VirtualHost>
```

`LoadModule`、`WSGIPythonHome`の記述は`mod_wsgi-express module-config`の出力を参考に適宜変更してください。
`WSGIScriptAlias`によって当該の`wsgi.py`を探させます。


ここまでできたらapacheを起動します。`C:\Apache24\bin`の作業ディレクトリで`httpd -k start`のコマンドで起動します。

ブラウザで`http://localhost:80`にアクセスすると、`wsgi.py`で記述した通りに`apache ok`と表示されます。
<img width="901" alt="apache.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186620/53ce6be0-9327-7395-ed04-6a6bb717f8a7.png">


### 注意点
`application = bottle.default_app()`によりWSGI仕様にソースを改良しています。なので、コード内をWSGI仕様にしなければ外部接続ができません。また`application`を`app`とかにすると読み込みができません。
**`mod_wsgi`はウェブサーバとPythonコードを結びつけるために`application`変数を探します。**