<jupyter_start><jupyter_text>1　基本構成<jupyter_code>from flask import Flask
from flask_restful import Resource, Api<jupyter_output><empty_output><jupyter_text>`Resource` 用來處理 API 使用到的物件<jupyter_code>app = Flask(__name__)
api = Api(app)<jupyter_output><empty_output><jupyter_text>創建一個 `Api` 物件並引入原先的 `Flask` 物件<jupyter_code>class Student(Resource):
    def get(self, name):
        return {'student': name}<jupyter_output><empty_output><jupyter_text>`Student` 類別會繼承(inheritance) `Resource` 類別函式名稱直接命名為 HTTP 方法 get<jupyter_code>api.add_resource(Student, '/student/<string:name>')

app.run(port=5000)<jupyter_output><empty_output><jupyter_text>將 resource 加入 API 當中，並指定 `Student` 類別及 endpoint 路徑如此可在 Postman 當中以 GET http://127.0.0.1:5000/student/Rolf 存取會回傳 `{"student": "Rolf"}`註：使用 Flask-RESRTful 會自動轉換成 JSON，不用再使用 `jsonify` ***  2-1　商店 API 實作例 - GET、POST<jupyter_code>class Item(Resource):
    def get(self, name):
        item = next(filter(lambda x:x['name'] == name, items), None)
        return {'item': item}, 200 if item else 404<jupyter_output><empty_output><jupyter_text>其中 `filter(lambda x: x['name'] == name, items)` 的意思同下列程式碼：<jupyter_code>for item in items:
    if item['name'] == name:
        return item<jupyter_output><empty_output><jupyter_text>`lambda` 函式的功能是 x 輸入後將 `x['name'] == name` 的真偽值輸出`filter` 函式的功能是將 `items` 此商品 list 中的元素丟入指定函式中，若結果為真則保留如此，滿足 `item['name'] == name` 的元素(此處為單一商品 dict)會被保留下來`next` 函式的作用為尋找下一個元素，若無則輸出預設值 `None`亦可使用 `list(filter())` 來回傳所有過濾後的元素列表 而在 return 後面的 200 為 HTTP 狀態碼，常見的有：- 200 OK (最常見)- 201 CREATED- 202 ACCPETED- 400 BAD REQUEST- 401 UNAUTHORIZED- 403 FORBIDDEN- 404 NOT FOUND- 500 INTERNAL SERVER ERROR其中 400 系列為使用者造成的錯誤、500 系列為伺服器本身的錯誤<jupyter_code>def post(self, name):
        data = request.get_json()
        if next(filter(lambda x: x['name'] == name, items), None) is not None:
            return {'message': "An item with name '{}' already exists."\
                    .format(name)}, 400

        item = {'name': name, 'price': data['price']}
        items.append(item)
        return item<jupyter_output><empty_output><jupyter_text>註： `is not None` 可以直接省略  2-2　商店 API 實作例 - DELETE、PUT<jupyter_code>def delete(self, name):
        global items
        items = list(filter(lambda x: x['name'] != name, items))
        return {'message': 'Item deleted'}<jupyter_output><empty_output><jupyter_text>用 `filter` 函式將 `items` 過濾，只留下指定名稱之外的物件，達成刪除如果不加 `global items` 指定使用全域的 `items`Python 會認為使用的是 `delete` 函式中的 `items`兩個 `items` 在同一行，會造成未定義就先使用的情況，產生錯誤<jupyter_code>def put(self, name):
        data = request.get_json()
        item = next(filter(lambda x: x['name'] == name, items), None)
        if item is None:
            item = {'name': name, 'price': data['price']}
            items.append(item)
        else:
            item.update(data)
        return item<jupyter_output><empty_output><jupyter_text>dict 有 `.update(data)` 函式可使用註：PUT 具有冪等 (idempotent) 特性，意指無論執行幾次，最後都會得到相同結果重複執行 PUT 指令不會額外創造出多個物件，但 POST 則有可能 ***  3-1　驗證功能 - 設定<jupyter_code>from flask_jwt import JWT, jwt_required, current_identity

app = Flask(__name__)
app.secret_key = 'wolf'
api = Api(app)<jupyter_output><empty_output><jupyter_text>JWT 代表 JSON Web Token須設定 `secret_key` 以供 `Flask_jwt` 進行加密為了執行驗證功能，需要另外兩個檔案：user.py 及 security.py - user.py<jupyter_code>class User:
    def __init__(self, _id, username, password):
        self.id = _id
        self.username = username
        self.password = password<jupyter_output><empty_output><jupyter_text>id 是 python keyword，故使用 `_id` - security.py<jupyter_code>from user import User

users = [
    User(1, 'Starlord', 'infinitygauntlet')
]

username_mapping = {u.username: u for u in users}
userid_mapping = {u.id: u for u in users}<jupyter_output><empty_output><jupyter_text>`{u.username: u for u in users}` 是 dict comprehension如上 `username_mapping` 的值會是 `{'bob': User(1, 'bob', 'asdf')}` 、格式為 dict<jupyter_code>def authenticate(username, password):
    user = username_mapping.get(username, None)
    if user and user.password == password:
        return user

def identity(payload):
    user_id = payload['identity']
    return userid_mapping.get(user_id, None)<jupyter_output><empty_output><jupyter_text>security.py 需要包含 `authenticate` 及 `identity` 兩個函式在這兩個函式中會使用 `dict.get(key, default)` 以 `username` 及 `user_id` 作為鍵值 (key) 對上述兩個 mapping dict 進行搜尋不直接使用 `['key']` 而使用 `.get()` 的原因是可以指定 找不到時 的預設回傳值 註：若使用 Python 2.7 或更早版本，不建議直接比對字串，建議使用如下方法：<jupyter_code>from werkzeung.security import safe_str_cmp

if user and safe_str_cmp(user.password, password):
    pass<jupyter_output><empty_output><jupyter_text>3-2　驗證功能 - 實作<jupyter_code>from security import authenticate, identity
from flask_jwt import JWT, jwt_required, current_identity

jwt = JWT(app, authenticate, identity)<jupyter_output><empty_output><jupyter_text>`jwt_required` 用以加入驗證用 decorator 到函式上`JWT()` 會創造路徑為 /auth 的 endpoint<jupyter_code>@jwt_required()
    def get(self, name):
        pass<jupyter_output><empty_output><jupyter_text>在函式上加入 `@jwt_required()` decorator 可讓此函式加上驗證保護 ***  4　輸入資料修剪 (Parse)<jupyter_code>from flask_restful import Resource, Api, reqparse

def put(self, name):
    parser = reqparse.RequestParser()
    parser.add_argument('price',
        type=float,
        required=True,
        help="This field cannot be left blank!"
    )
    data = parser.parse_args()<jupyter_output><empty_output><jupyter_text>`parser = reqparse.RequestParser()` 建立一個新的 parser`parser.add_argument()` 加入 parser 修剪對象 (key) 及條件`data = parser.parse_args()` 針對設定對象執行修剪如此不論使用者傳入的 JSON 包含什麼資料，都會被修剪成只剩下指定的資料註：此函式可用於修剪 HTML 的 Form 通常 parser 會定義在主類別之下，避免在函式中重複複製貼上故可將程式碼修改如下：<jupyter_code>class Item(Resource):
    parser = reqparse.RequestParser()
    parser.add_argument('price',
        type=float,
        required=True,
        help="This field cannot be left blank!"
    )

    def post(self, name):
        if next(filter(lambda x: x['name'] == name, items), None) is not None:
            return {'message': "An item with name '{}' already exists.".format(name)}

        data = Item.parser.parse_args()
        item = {'name': name, 'price': data['price']}
        items.append(item)
        return item<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12894864733516928, 'fraction_numerical': 0.01133970516766564, 'mean_word_length': 4.852132701421801, 'pattern_counts': {'":': 1, '<': 74, '<?xml version=': 0, '>': 74, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 1, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '18', 'dir': 'jupyter-structured-clean-dedup', 'id': '608379', 'license': 'Apache-2.0', 'n_tokens_mistral': 2791, 'n_tokens_neox': 2840, 'n_words': 496, 'path': 'Flask - RESTful.ipynb', 'repo_name': 'cokewolf/Python_web_notebooks', 'size': '6173'}