Fast API 활용 2

DONGJIN IM·2022년 7월 12일
0

Product Serving 이론

목록 보기
9/10
post-custom-banner

Fast API 활용 2

Form이란?

  • 입력 형태로 데이터를 받고 싶을 경우 활용하는 모듈

    • 입력 형태 : Frontend의 HTML이나 Javascript 쪽에서 타이핑한 입력값을 활용한 데이터
    • (ex) 로그인할 때 보내는 데이터를 Request Body나 Response Body를 설정해서 받을 수도 있지만, Frontend 파일에서 입력 위치에 따라 자동으로 Body를 생성하여 받고 싶을 때 활용
  • python-multipart가 필요함

    • pip install python-multipart
  • Frontend도 간단히 만들기 위해 Jinja2 설치

    • pip install Jinja2
    • 활용해 본 결과, Mustache와 매우 유사

Form 활용

Frontend(HTML) 파일

# 파일 이름 : ex.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Sample Login</title>
</head>
<body>
<form method="post">
    <input type="string" name="username" value="{{username}}"/>
    <input type="password" name="password" value="{{password}}"/>

    <input type="submit">
</form>
</body>
</html>
  • value="{{username}}" : Key는 username이며, 해당 input 공간에 입력된 값이 Value가 됨

  • value="{{password}}" : Key는 password이며, 해당 input 공간에 입력된 값이 Value가 됨

  • <input type='submit'> : 따로 POST Mapping할 URI가 주어지지 않았으므로, 해당 HTML 파일을 띄우는 URI 그대로 POST 요청을 보낼 것

    • 아래 코드에서 /login을 하면 해당 로그인 창이 뜨기 때문에, submit 버튼을 누르면 POST 방식으로 /login에 요청을 보냄

Backend(Fast API를 활용한 Python) 파일

from fastapi import FastAPI, Form, Request
from fastapi.templating import Jinja2Templates

import uvicorn

app = FastAPI()
templates = Jinja2Templates(directory='./')

@app.get('/login')
def get_login_form(request: Request):
    return templates.TemplateResponse('ex.html', 
                            context={'request':request})
# /login을 입력하면 ex.html과 연결하는 것

@app.post('/login')
def login(username:str=Form(...), password:str=Form(...)):
    return {"username":username, "password":password}

if __name__=='__main__':
    uvicorn.run(app, host='0.0.0.0', port = 8000)
  • localhost:8000/login을 접속하면 일어나는 과정들
  1. get_login_form 함수에 접근함

  2. templates.TemplateResopnse() 메서드에 의해 ex.html로 이동함

  3. ex.html 창이 Frontend로 뜨게 됨

  4. ID와 Password를 입력하고 Submit 버튼을 누르면 POST Method로써 /login에 요청이 가므로, login() 메서드가 수행됨

  5. login() 메서드는 {"username":username, "password":password} 형식 데이터를 반환하므로, 아래 사진과 같은 결과를 반환함

  • 중요한 점!

    • templates.TemplateResponse() 메서드를 수행시킬 때 꼭 contex={'request':Request'}를 통해 get_login_form 메서드가 받은 Request를 전달해 줘야 함
  • Form(...) : Required(필수 요소)

    • 꼭 입력되어야 하는 데이터
    • 만약 Form(...)인 데이터가 입력되지 않으면 아래 사진과 같은 에러가 발생한다

templates.TemplateResponse()

"어떤 Frontend 파일"에 "어떤 데이터"를 보낼 것인지 명시하는 메서드이다.
위에서는 Request 객체를 ex.html에 보낸다는 의미이다.
그렇다면, Request 객체 이외에 다른 임의 데이터도 HTML 등에 보낼 수 있을까?
정답은, 가능하다!
아래 코드 예시와 결과를 통해 확인하자

ex.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Sample Login</title>
</head>
<body>
<form method="post">
    <input type="string" name="username" value="{{username}}"/>
    <input type="password" name="password" value="{{password}}"/>
    
    # 이 부분만 추가됨
    # 즉, id라는 값을 받아서 Frontend에 띄우는 것
    <input type="text" value="{{id}}" disabled>

    <input type="submit">
</form>
</body>
</html>

Python 파일

from fastapi import FastAPI, Form, Request
from fastapi.templating import Jinja2Templates

import uvicorn

app = FastAPI()
templates = Jinja2Templates(directory='./')

# 바뀐 부분. context에 Request만 보내는게 아니라 "id":2라는 새로운 값, 쌍을 포함시켜 보냄
@app.get('/login')
def get_login_form(request: Request):
    return templates.TemplateResponse('ex.html', context={'request':request, "id":2})

@app.post('/login')
def login(username:str=Form(...), password:str=Form(...)):
    return {"username":username, "password":password}

if __name__=='__main__':
    uvicorn.run(app, host='0.0.0.0', port = 8000)

  • 위 사진에서 context에 포함시킨 "id":2라는 값이 적용되어 Jinja2의 value="{{id}}"부분이 채워졌음을 알 수 있다. 즉, Request 뿐만이 아닌 내가 원하는 데이터로 미리 Frontend 파일을 채울 수도 있다는 것이다

File

  • HTML에서 <form action='/files' enctype='multipart/form-data' method='post'>를 통해 어떤 URI에 어떤 데이터를 넘길지 명명함

  • File이 넘어온 데이터는 from fastapi import File을 통해 상속받을 수 있는 File(...)을 통해 받을 수 있음

from typing import List

from fastapi import FastAPI, File, UploadFile

import uvicorn

app = FastAPI()

@app.post("/files")
def create(files:List[bytes] = File(...)):
	return {"file_sizes":[len(file) for file in files]}
    
@app.post("/uploadedfiles")
def create_uploaded(files:List[UploadFile] = File(...)):
	return {"file_sizes":[file.filename for file in files]}
    
...
  • create() : File(...)을 통해 File 객체를 받을 수 있음을 알 수 있다. 하지만, UploadFile 객체를 받는 것이 아니라 bytes 형태로 File을 받는다.

  • create_upload() : 동일하게 File 객체를 받음을 알 수 있다. 이 때에는 UploadFile의 List형식을 받는다고 되어 있다.
    즉, User가 Upload한 파일 그 자체를 넘겨받는 것이다.
    따라서, File의 Name인 file.filename도 접근할 수 있게 되는 것이다
    (create() 메서드에서는 filename을 알 수는 없음. 데이터 자체가 Bytes로 변환되어 오기 때문)

profile
개념부터 확실히!
post-custom-banner

1개의 댓글

comment-user-thumbnail
2022년 10월 11일

아 이 내용을 딱 찾고있었는데 정말 감사드립니다

답글 달기