# 後端介接前端 Backend with FastAPI - *Through the Galaxy*
###### tags: `backend`
Copyright 2021, [月下麒麟](https://hackmd.io/@YMont/note-catalog)
---
## Target
>浩瀚無垠的宇宙,人類用盡許多方法向這廣袤的星系呼喊
>嘗試了一百萬年後,終於找到了規則與方法
>接收到了外星人的回應...
==今天要講述的是後端該如何接收前端丟過來的資料==
## Concept
* 前端要建立一個**POST**的請求(with jQuery and AJAX)
* 後端要建立一個**Route**去接收前端的請求
補充:
可以了解前端如何發送請求,以及技術工具的演進
reference:[什麼是 Ajax? 搞懂非同步請求 (Asynchronous request)概念](https://tw.alphacamp.co/blog/ajax-asynchronous-request)
reference:[[JS] AJAX 筆記](https://medium.com/%E9%A6%AC%E6%A0%BC%E8%95%BE%E7%89%B9%E7%9A%84%E5%86%92%E9%9A%AA%E8%80%85%E6%97%A5%E8%AA%8C/js-ajax-%E7%AD%86%E8%A8%98-b9a57976fa60)
筆者是一個在學技術前,會想先知道這個工具的前後歷史
知道了它的演替後,再爬技術文章閱讀時,才能知道該作者用了哪一套工具、語法
因前端工具與框架繁多,特別在學前端時,更有這個感觸
瞭解了前因後果,才能對網路上或是書本上的資訊分門別類
最後再**篩選**出自己要的資訊,並加以學習**實作**
==文章會以漸進式的方式敘述,順便記錄下我的Try & Error==

(引用網路圖片)
很喜歡排球少年-稻荷崎的應援橫幅的標語 **昨日無須追憶**
## Method
### Part1
先來個基礎原型
frontend
```javascript=
var arr = {id:12 ,
name:"TT",
height:178.5,
weight:69.2,
habbit:"router"}
$.ajax({
url:"http://localhost:8000/index",
type: "POST",
dataType: "json",
data: JSON.stringify(arr),
success: function(res){
console.log(res);
},
error: function(error){
console.log(error)
}
});
```
這樣的寫法比較沒什麼懸念,唯一要注意的是我有**把資料轉成JSON格式(JSON.stringify)**
backend
如下程式是可以運作的
```python=
@app.post("/index",)
async def test(res:Request):
print(res.method) # for uvicorn server
res = await res.body()
print(res) # for uvicorn server
return res # for frontend web console log
```
* 函數參數位置,**res是變數**,**Request是fastapi的請求模組**[Using the Request Directly](https://fastapi.tiangolo.com/advanced/using-request-directly/)
是個封裝後非常有趣的模組,試想,fastapi在背後做了多少的動作,才完成這個請求!
* 一樣的程式,拿掉**async**、**await**,就會報錯
從uvicorn server端觀察

可以發現POST字眼,程式第3行有執行,後面就報錯了
從react web console log觀察

這邊花了一些時間釐清
接著把return的內容改成"T.T",沒有了async、await、return一個str
```python
@app.post("/index",)
async def test(res:Request):
print(res.method)
res = await res.body()
print(res)
return "T.T"
```
會得到一個warning,關於**RuntimeWarning**

```lib\site-packages\anyio\_backends\_asyncio.py:743: RuntimeWarning: coroutine 'Request.body' was never awaited```
原來就是要加入async、await,讓請求的機制完成
web console log上看到的資料

==在做前後端介接時,需要反覆trace error,查看兩邊的terminal,才能查到問題==
---
### Part2
**目標**

**我想讓上圖的結果變成下圖**

>簡言之,把後端return出來的內容可以變成Javascript的物件(object)
frontend
```javascript=
var arr = {id:12 ,
name:"TT",
height:178.5,
weight:69.2,
habbit:"router"}
$.ajax({
url:"http://localhost:8000/index",
type: "POST",
headers:{"Access-Control-Allow-Origin":"*",
"Access-Control-Allow-Methods":"*",
"Access-Control-Allow-Headers":"*",
"Access-Control-Allow-Credentials":"true",
"Content-Type":"application/json"},
dataType: "json",
data: JSON.stringify(arr),//body
success: function(res){
console.log(res);
},
error: function(error){
console.log(error)
}
});
```
backend-1
```python=
from pydantic import BaseModel
class PeopleBase(BaseModel):
id: int
name: str
height: float
weight: float
habbit: str
```
backend-2
```python=
@app.post("/index",response_model=PeopleBase)
async def test(res:PeopleBase):
print(res)
return res
```
部分程式省略,建議可看[後端學習紀錄 Backend with FastAPI - Website with Departure](https://hackmd.io/@YMont/python-fastapi-2)
* 眼尖的你可能有注意到,在backend-2 \@app.post的路由後面還有加入**response_model=PeopleBase**
其實它是用來做資料格式檢查用的,為fastapi的**pydantic**
這樣就能讓return後的結果為JSON格式的物件
reference:[pydantic](https://pydantic-docs.helpmanual.io/)、[Just Modern Python](https://fastapi.tiangolo.com/features/#just-modern-python)
* 眼尖的你,也發現到這次我們加了許多**header**進去
至於前端若以上一個範例去操作,會得到一個錯誤訊息

提示我們,這樣的格式是有問題的
這邊筆者算是嘗試出來的,尚未對這塊的原理做理解
---
### Part3
最後,我們都能成功取得資料,也把格式都轉對了
當然,後端接收到資料,就是要**將資料寫進去資料庫**啊~
backend
```python=
# main.py
@app.post("/index")
async def method_Create(request: UserUpdate, db:Session= Depends(get_db)):
try:
res = await crud.create_user(db,request)
return res
except Exception as err:
return HTTPException(**err.__dict__)
```
```python=
# schemas.py
from typing import List
from pydantic import BaseModel
class PeopleBase(BaseModel):
id: int
name: str
height: float
weight: float
habbit: str
class PeopleUpdate(PeopleBase):
class Config:
orm_mode = True
```
這邊就不著墨各個模組細節
其大意為用UserBase為取得前端資料來源,建立具有DB空欄位的物件
接著,await crud裡的函式create_user去建立我們設定的資料
希望今天閱讀到這裡的你也會有收穫!