iT邦幫忙

2022 iThome 鐵人賽

DAY 29
0
自我挑戰組

從前端角度看30天學Python系列 第 29

【Day 29】打造一個 API

  • 分享至 

  • xImage
  •  
  • 使用 GET 取得資料
    • 取得 MongoDB 上的資料
    • 透過 id 取得資料
  • 使用 POST 新增資料
  • 使用 PUT 更新資料
  • 使用 DELETE 刪除資料

這篇文章是閱讀Asabeneh的30 Days Of Python: Day 29 - Building an API後的學習筆記與心得。

因為原文中 Day 28 - API 主要在說明 API 是什麼,基於前端應該都很熟悉這個,就直接來看原文的 Day 29 實際寫一個 API 了。另一方面,就算不熟悉 API 定義,實際看過它運作應該也比較好了解這個概念。


這章會使用 Day 27 及 Day 28 使用的 Flask 框架及 MongoDB 來實作 RESTful API 對應 HTTP 請求方法中的 GET、PUT,POST 和 DELETE 來操作資料。

什麼是 RESTful API,可以參考 AWS 的這篇文章。

輔助工具: Postman
這篇文章最後做出來的 API server 檔案:https://github.com/AlliesChen/flask-api

使用 GET 取得資料

  1. 首先需要一個接收 GET 請求的地方,也就是伺服器,這邊使用 Flask
  2. 再來要有一個位址 (path) 讓使用者拜訪,也就是route
  3. 建立資料後,使用 flask 模組中的 Response 類別來回傳資料給使用者,回傳資料的格式需要用到 Python 內建的 json 模組來轉成 JSON 格式 (直接回傳 student_list 是會報錯的)。
from flask import Flask, Response
import json
import os

app = Flask(__name__)

@app.route("/api/v1.0/students", methods = ["GET"])
def students():
    student_list = [
        {
            'name':'Asabeneh',
            'country':'Finland',
            'city':'Helsinki',
            'skills':['HTML', 'CSS','JavaScript','Python']
        },
        {
            'name':'David',
            'country':'UK',
            'city':'London',
            'skills':['Python','MongoDB']
        },
        {
            'name':'John',
            'country':'Sweden',
            'city':'Stockholm',
            'skills':['Java','C#']
        }
    ]

    return Response(json.dumps(student_list), mimetype="application/json")


if __name__ == "__main__":
    port = int(os.environ.get("PORT", 5000))
    app.run(debug=True, host="0.0.0.0", port=port)

可以透過 Postman (本地端) 版本,向 http://localhost:5000/api/v1.0/students 使用 GET,會拿到:

[
    {
        "name": "Asabeneh",
        "country": "Finland",
        "city": "Helsinki",
        "skills": [
            "HTML",
            "CSS",
            "JavaScript",
            "Python"
        ]
    },
    {
        "name": "David",
        "country": "UK",
        "city": "London",
        "skills": [
            "Python",
            "MongoDB"
        ]
    },
    {
        "name": "John",
        "country": "Sweden",
        "city": "Stockholm",
        "skills": [
            "Java",
            "C#"
        ]
    }
]

取得 MongoDB 上的資料

上面的例子是回傳寫死的 data 給使用者,在 Day 28 有實作過跟 MongoDB 互動,讓回傳的資料變成動態的:

  • MongoDB 回傳的資料是 BSON -- 關於JSON和BSON
  • 需要引入 bson.json_utildumps 方法處理 BSON -- 參考這篇文章
  • 寫一個 initiate_data 函式判斷 students 不存在的話新增資料到 MongoDB,存在的話則不做任何事。
  • 因為要跟外部的 API 互動,為了避免非預期的錯誤,使用了 try...except—在 Day 17 有學過。
from flask import Flask, Response
from bson.json_util import dumps
import os
from pymongo import MongoClient

app = Flask(__name__)

MONGODB_URI = "mongodb+srv://username:your_password@30daysofpython-twxkr.mongodb.net/?retryWrites=true&w=majority"
client = MongoClient(MONGODB_URI)
db = client["thirty_days_of_python"]


def initiate_data():
    if "students" in db.list_collection_names():
        print("db is ready")
        return None
        
    students = [
        {
            'name': 'Asabeneh',
            'country': 'Finland',
            'city': 'Helsinki',
            'skills': ['HTML', 'CSS', 'JavaScript', 'Python']
        },
        {
            'name': 'David',
            'country': 'UK',
            'city': 'London',
            'skills': ['Python', 'MongoDB']
        },
        {
            'name': 'John',
            'country': 'Sweden',
            'city': 'Stockholm',
            'skills': ['Java', 'C#']
        }
    ]
    try:
	    db.students.insert_many(students)
        print("Adding init data to db")

    except Exception as err:
        print(f"Something wrong while initiating data - Error: {err}")
    
    return None

@app.route("/api/v1.0/students", methods=["GET"])
def students():
    try:
        students = db.students.find()
        json_data = dumps(students)
        
        return Response(json_data, mimetype="application/json")
    
    except Exception as err:
        
        return f"Something wrong while GETting students - Error: {err}"


if __name__ == "__main__":
    initiate_data()
    port = int(os.environ.get("PORT", 5000))
    app.run(debug=True, host="0.0.0.0", port=port)

現在訪問 http://localhost:5000/api/v1.0/students 一樣能拿到前面的資料,但資料現在是存在 MongoDB 上。

透過 id 取得資料

如果只想要單筆資料,可以透過判斷 URL 中是否有加入資料 id 的方式,像是 http://localhost:5000/api/v1.0/students/<id>

  • 在 route 的 網址中加入 <id>,這樣就能取得使用者想拜訪的 id。
  • 需要再引入 bson.objectid 中的 ObjectId 這個類別,讓我們能在 MongoDB 查找這個物件。
from bson.objectid import ObjectId

# 中間沒變的程式碼省略

@app.route("/api/v1.0/students/<id>", methods=["GET"])
def single_student(id):
    try:
        student = db.students.find({"_id": ObjectId(id)})
        json_data = dumps(student)
        
        return Response(json_data, mimetype="application/json")
    except Exception as err:
        
        return f"Something wrong while GETting student - Error: {err}"

使用 POST 新增資料

這邊必須搭配 Postman 使用 POST 方法,因為瀏覽器不透過 JavaScript 的話,只能做 GET:

Postman 要選 POST,然後 Body,選擇 raw,並且必須是 JSON:

{"name": "Nanako", "country": "Japan", "city": "Tokyo", "skills": ["HTML", "CSS", "Vue"]}

然後在 app.py

  1. flask 中再引入 request 這個物件,要透過 request.json 來拿到要新增的資料。 -- 參考這則回答
  2. 修改 students 這個函式的內容,當 request.method == "POST" 的時候,要對資料庫新增內容。
  3. insert_one 的回傳值中有被新增物件的 id,透過這個 inserted_id 再配合 find_one({"_id": <id>}) 去回傳新增的物件。
from flask import request

@app.route("/api/v1.0/students", methods=["GET", "POST"])
def students():
    if request.method == "POST":
        student = request.json
        print(student)

        try:
            result = db.students.insert_one(student)
            new_student = db.students.find_one({"_id": result.inserted_id})
            json_data = dumps(new_student)

            return Response(json_data, mimetype="application/json")

        except Exception as err:
            return f"Something wrong while POSTing students - Error: {err}"

    else:
        try:
            students = db.students.find()
            json_data = dumps(students)

            return Response(json_data, mimetype="application/json")

        except Exception as err:

            return f"Something wrong while GETting students - Error: {err}"

使用 PUT 更新資料

這邊一樣借助 Postman,這次方法選擇 PUT,然後要把網址的末端加上要改的 id,比如: http://localhost:5000/api/v1.0/students/6347d13fd24d777606b2a399

然後要改的內容,一樣必須是選擇 JSON,這邊改的是 cityskills 的內容:

{"name": "Nanako", "country": "Japan", "city": "Osaka", "skills": ["HTML", "CSS", "Vue", "Sass"]}

app.py 新增一個段落,可以從 single_student—透過 id 取得資料,複製做修改:

  • methods 改為 PUT。
  • 參考 day 28 提到過的 update_one 使用方法。
@app.route("/api/v1.0/students/<id>", methods=["PUT"])
def update_student(id):
    query = {"_id": ObjectId(id)}
    new_value = {"$set": request.json}

    try:
        db.students.update_one(query, new_value)
        updated_value = db.students.find_one(query)
        json_data = dumps(updated_value)

        return Response(json_data, mimetype="application/json")

    except Exception as err:

        return f"Something wrong while updating student's info - Error: {err}"

使用 DELETE 刪除資料

這個段落跟 PUT 的寫法相像:

  • methods 改成 DELETE
  • update_one 方法換成 delete_one(filter)id 做為 filter 參數。
  • 回傳改為回傳尚存的項目,使用 find()
@app.route("/api/v1.0/students/<id>", methods=["DELETE"])
def delete_student(id):
    query = {"_id": ObjectId(id)}

    try:
        db.students.delete_one(query)
        updated_data = db.students.find()
        json_data = dumps(updated_data)

        return Response(json_data, mimetype="application/json")

    except Exception as err:

        return f"Something wrong while DELETing student's info - Error: {err}"

上一篇
【Day 28】Python with MongoDB
下一篇
【Day 30】結語
系列文
從前端角度看30天學Python30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言