モダンでフレンドリーな Responder入門 と調べても中々出てこない情報達

[Responder](https://github.com/kennethreitz/responder)とはRequestsやpipenvの作者である[kennethreitz](https://github.com/kennethreitz)氏によるモダンなwebフレームワークです。

FlaskとFalconの良い部分を合わせるというのがアイデアのようで、確かに多くの機能を受け継いでいます。

さて、では初めて行きましょう。

# 基礎

まずは、`Responder.API`を宣言するところから始めます。

```python
import responder

api = responder.API()
```

インスタンス名はFlaskのような`app`ではなく`api`とするのがマナー(？)のようです。

## ルーティング
ルートを定義するには、`@api.route("route_name")`で関数をデコレートするか、`api.add_route("route_name", callback)`で関数をcallbackとして与えることができます。
基本的にはデコレートするほうがわかりやすいと思います。
ファイルが大きくなってきて、ルートを別ファイルに記述するようなときは、callbackとして与える方法ももちろん良いでしょう。

```python
@api.route("/main_page")
def main_page(req, resp):
    resp.text= "Thanks for accessing"  #-------------  text
    resp.media = {"success": True}  #----------------  json, yaml
    resp.html = "<p>Thanks for accessing</p>"  #-----  html
    resp.status_code = 200  #------------------------  http status
    resp.headers["X-Pizza"] = "42"  #----------------  header
```

このように、`resp`にデータを与えることでレスポンスを操作します。
基本的に`resp.status_code`は設定しなければ自動で`200`となります。ですので、必要な情報のみ与えてください。

また、ルーティングには、f文字列式の動的ルーティングを使うことができます。

```python
@api.route("/main_page/{name}")
def main_page(req, resp, *, name):
    resp.text= f"Thanks for accessing, {name}"
```

callbackにはキーワード引数で与えられるので、上記のようにしてください。

## 非同期・バックグラウンド実行
`await`, `async`, `@api.background.task`を用いることで、バックグラウンドで処理を実行することができます。

```python
@api.route("/taking")
async def some_function(req, resp):

    @api.background.task
    def take_long_time():
        # 時間のかかる処理

    take_long_time()

    resp.media = {'success': True}
```

## 実行
`api.run()`を実行してください。

```python
if __name__ == '__main__':
    api.run()
```

# なかなか出てこない情報達
## port, address, デバッグモードの変え方
`api.run()`の引数に値を与えてください。

```python
api.run(port=8080, address="127.0.0.2", debug=True)
```
そのほかにも、デバッグレベルなど沢山の引数を取ります。それらは、[uvicornのsetting](https://www.uvicorn.org/settings/)をご覧ください。

## リクエストのメソッド確認
`req.method`を見てください。

```python
@api.route("/check_method")
def check_method(req, resp):
    if req.method == "get":
        resp.text = "this is get"
    elif req.method == "post":
        resp.text = "this is post"
```

## postデータの受け取り方
`await req.media()`をasyncで定義したコルーチンの中で呼び出してください。

```python
@api.route("/take_post")
async def take_post(req, resp):
    if req.method == "get":
        resp.text = "please send post"
    else:
        data = await req.media()
        resp.media = data
        resp.text = "I'm giving you back :) "
```

`req.media()`の引数に`format="form"`や`format="json"`のように指定することで、受け取るものを選ぶことができます。

## Cookieに値を設定する、取得する
`api.session`・`req.cookies`で取得、`api.session`・`resp.cookies`で設定。細かく設定するには`resp.set_cookie`を使用しましょう。
`api.session`・`req.cookies`・`api.session`は辞書で提供されるので、簡単に取得、設定することができます。
`resp.set_cookie`では、以下の値を引数に取ります。

- _key_ : cookieの名前です
- _value_ : 実際に保存するデータです
- _expires_ : この cookie が破棄される日付です
- _max_age_ : この cookie が破棄されるまでの秒数です
- _secure_ : https のみで利用できるよう制限するかです
- _httponly_ : http の内部から(javascriptなどを利用して)閲覧できないよう制限するかです

## Login Management
ResponderにはFlask-loginのようなログインマネージャは存在しません。
ですから、作りました。(隠しきれないステマ)
[Responder-Login](https://github.com/delta114514/responder-login) --- [Docs](https://responder-login.readthedocs.io/en/latest/)
下記のように使用できます。

```python
import responder
from responder_login import LoginManager, UserMixin

api = responder.API()
lm = LoginManager(api)

users = []


class User(UserMixin):
    id = None
    name = "Anonymous"
    age = 0

    def __init__(self, name, age):
        global users
        self.name = name
        self.age = age
        users.append(self)
        self.id = users.index(self)

    def get_id(self):
        return self.id

@lm.user_loader
def user_loader(user_id):
    try:
        return users[int(user_id)]
    except IndexError:
        return None


@api.route("/login/{name}/{age}")
@lm.login_prohibited
def login(req, resp, *, name, age):
    user = User(name, age)
    lm.login_user(user)
    resp.text = f"you've logged in as User; name: {user.name}, age: {user.age}"


@api.route("/show")
def show(req, resp):
    user = lm.current_user
    if user.is_authenticated:
        resp.text = f"you're logging in as User; name: {user.name}, age: {user.age}"
    else:
        resp.text = "you're not logging in"


@api.route("/logout")
@lm.login_required
def logout(req, resp):
    lm.logout_user()
    resp.text = "you've logged out"


if __name__ == "__main__":
    api.run()
```

ガバガバ英語と少ない機能、不適な実装だらけですので、プルリクを沢山送って、issueを沢山立ててもらえると嬉しいです。