images
29/10/2020 03:50 am

Python ứng dụng: Tạo Rest API bằng Flask

REST viết tắt của REpresentational State Transfer. Đây là phong cách thiết kế hệ thống phân tán siêu phương tiện (distributed hypermedia systems) được giới thiệu đầu tiên năm 2000 bởi Roy Fielding trong nghiên cứu của mình.

WEB APPLICATION 

RESTFUL API


REST viết tắt của REpresentational State Transfer. Đây là phong cách thiết kế hệ thống phân tán siêu phương tiện (distributed hypermedia systems) được giới thiệu đầu tiên năm 2000 bởi Roy Fielding trong nghiên cứu của mình.


Resource: Nguyên tắc dựa trên các resource. Ví dụ: bài Post, thực thể Employee, thực thể Books… 


- Sử dụng Id để định danh resource, ví dụ: /books/1 → Book có id = 1.


- Data format có thể nhiều dạng: Html, Json, XML,.... Tuỳ thuộc vào client yêu cầu


- Restful còn biết đến như là một phương thức phi trạng thái (Stateless). Điều đó có nghĩa là khác với ứng dụng Web thông thường, với Restful không có khái niệm về Session.


Resource Method: Dự trên giao thức của HTTP nên có các method sau:


- GET: Lấy dữ liệu

- POST: Tạo dữ liệu

- PUT: Tạo hoặc update dữ liệu

- DELETE: Xoá dữ liệu



Flask là framework được sử dụng để phát triển ứng dụng web với ngôn ngữ Python. Flask đóng gói thành thư viện nhẹ và có cú pháp đơn giản Sử dụng Flask để tạo ứng dụng web và API rất nhanh và tiện lợi.


PYTHON API WITH FLASK


Trước tiên bạn cần cài đặt Flask.


pip install Flask


Sau đó, tạo file app.py với nội dung sau:


from flask import Flask

app = Flask(__name__)

@app.route("/")

def hello_world():

   return "Hello World!"

if __name__ == "__main__":

   app.run()


Sau đó bạn chạy lệnh python3 app.py: Ứng dụng sẽ chạy ở cổng 5000



* Serving Flask app "app" (lazy loading)

 * Environment: production

   WARNING: This is a development server. Do not use it in a production deployment.

   Use a production WSGI server instead.

 * Debug mode: off

 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)


Bạn vào trình duyệt và đánh vào địa chỉ localhost:5000




Giờ bạn hãy thay đổi dòng trả về thành Hello Techzonefun và chạy lại ứng dụng:


from flask import Flask

app = Flask(__name__)

@app.route("/")

def hello_world():

   return "Hello Techzonefun!"

if __name__ == "__main__":

   app.run()


Để thêm một api mới, bạn hãy dùng cú pháp: @app.route("/pathname") 

Ví dụ ta thêm path = languages 


@app.route("/languages")

def get_languages():

   return "python,java, golang"


Bạn vào trình duyệt và đánh vào địa chỉ localhost:5000/languages:



Lấy thông tin trong query param của request


Như vậy trong phần trên ta đã thấy, mỗi khi thêm một path mới, ta cần sử dụng decorator: @app.route.


Vậy nếu ta cần lấy query param trong request gửi lên thì sẽ làm cách nào?. Giả sử có request dạng sau: 


localhost:5000/languages?name=python&order=desc


Bạn cần import module request và sửa code như sau:


from flask import request

@app.route("/languages")

def get_languages():

  print(request.args)

  print(request.args.get('name'))

  return "python,java,golang"


Chúng ta thực hiện request:



Trong màn hình terminal, chúng ta sẽ có thấy 2 dòng sau được in ra khi thực hiện request trên


ImmutableMultiDict([('name', 'python'), ('order', 'desc')])


python


Như vậy chúng ta thấy: 


- request.args trả về một dictionary chứa các key-value tương ứng với request


- Để truy cập vào từng query value, chúng ta cần access qua key: request.args[“name"]


Lấy thông tin trong path của request


Giả sử bạn có request dạng sau: http://localhost:5000/languages/{name}/{des


Trong đó name và des là các giá trị có thể biến đổi do người dùng gửi lên trong request. Vậy làm thế nào ta lấy được giá trị này trong code. Bạn hãy dùng theo cú pháp sau để biến giá trị từ request thành tham số trong hàm:


@app.route("/languages/<name>/<des>")

def get_language_by_name(name, des):

  print(name, des)

  return "{} - {}".format(name,des)


Do khai báo như sau nên ta có thể lấy được các giá trị trong path để truyền vào hàm get_language_by_name(name, des)


@app.route("/languages/<name>/<des>")

def get_language_by_name(name, des):


HTTP METHOD


Bảng dưới đây giới thiệu về các method HTTP hay dùng. Đặc biệt sẽ dùng rất nhiều khi chúng ta sử dụng với Rest API.


Method

Giải thích

GET

Đây là phương thức thông dụng nhất của HTTP để lấy về dữ liệu

POST

Phương thức này được sử dụng để tạo dữ liệu: Ví dụ ta tạo bài viết, tạo 1 order...

PUT 

Phương thức PUT được sử dụng trong trường hợp muốn thay đổi dữ liệu hoặc thay thế toàn bộ dữ liệu cũ: Ví dụ sửa hoặc thay thế toàn bộ bài viết

PATCH 

PATCH được áp dụng cho trường hợp muốn thay đổi dữ liệu: thay đổi nội dung bài viết

DELETE

Xoá dữ liệu, ví dụ bạn muốn xoá bài viết.


HTTP STATUS CODE


Bên cạnh method, status code là tham số quan trọng để xạc định một request là thành công hay lỗi. Bản dưới dây liệt kê một số status code điển hình:


Status code

Giải thích

1xx

Biểu thị thông tin:

  • 100 Continue

  • 101 Switching Protocols

  • 102 Processing

2xx

Báo thành công:

3xx

Trạng thái về redirect

4xx

Các lỗi liên quan tới client

5xx

Lỗi xử lý liên quan tới server:


RESTFUL API VỚI HTTP METHOD


Trong phần trên ta chỉ làm việc với API với method là GET và thông tin trả về dạng raw text. Thực tế thì Restful API dựa trên HTTP nên nó có đủ các method của HTTP mà phổ biến là: GET, POST, PATCH, DELETE, PUT.

Như vậy trong Flask làm sao để khai báo các method này? Đơn giản ta sẽ sử dụng thêm tham số methods như sau:

@app.route("/languages", methods=['POST', 'GET'])

def get_languages():

Ở đây methods có thể khai báo thành danh sách. Với mỗi method này, thì khi ta thực hiện request với một trong các phương thức trong danh sách, thì hàm tương ứng sẽ được gọi. Nếu request với phương thức không được khai báo, hàm tương ứng sẽ không được gọi.


Restful API có thể trả về client với nhiều dạng dữ liệu khác nhau: text, csv, xml, json. Nhưng định dạng JSON hiện tại là định dạng phổ biến nhất khi trả về dữ liệu trong json. JSON là dạng dữ liệu thân thiện do dễ đọc, chứa dữ liệu dạng key-value hoặc dữ liệu mảng. JSON giúp dễ dang tích hợp các hệ thống, nên được dùng rộng rãi.

Ví dụ về json object:

{

  “name": “python",

  “author”: “Rossum”

}

Ví dụ về json array:

[“python", “golang", “java"]


JSON TRONG PYTHON


Ví dụ về json array of objects:


[{

  “name": “python",

  “author”: “Rossum”

},

{

  “name": “java",

  “author”: “Gosling”

}]



Trong Python, bạn có thể dụng module json để làm việc với python. 

1. Bạn có 1 dictionary và muốn convert nó thành dạng Python:

2. Bạn có 1 chuỗi json và muốn convert nó thành một Dictionary:


1. Bạn có 1 dictionary và muốn convert nó thành dạng Python, Đây gọi là quá trình Serialize:


import json

python_language = {

   "language": "python",

   "author": "Rossum"

}

json_string = json.dumps(python_language)

print(type(json_string))

print(json_string)


Dòng code trên với json.dumps(python_language) sẽ convert một Dictionary thành một xâu (kiểu str) dạng json. Bạn có thể xem kết quả khi chạy như sau:


<class 'str'>

{"language": "python", "author": "Rossum"}

2. Bạn có 1 chuỗi json và muốn convert nó thành một Dictionary: Đây còn gọi là quá trình Deserialize


import json


json_string = '{"language": "python", "author": "Rossum"}'

python_language = json.loads(json_string)

print(type(python_language))

print(python_language)


Kết quả: Bạn sẽ convert được một chuỗi có dạng json thành một object dạng Dictionary:

<class 'dict'>

{'language': 'python', 'author': 'Rossum'}


ÁP DỤNG FLASK ĐỂ XÂY DỰNG 1 RESTFUL API


Vậy ta sẽ thử áp dụng Flask để xây dựng 1 ứng dụng Restful API quản lý danh sách các ngôn ngữ lập trình như sau và trả về dữ liệu JSON.


API: /languages


Method

Giải thích ví dụ

POST

Tạo ngôn ngữ lập trình mới trong danh sách. Mỗi ngôn ngữ có dạng sau:

{“language": “python",

 “author": “Rossum"}

GET

Lấy danh sách ngôn ngữ. Ví dụ:

  • GET /languages: Trả về danh sách ngôn ngữ lập trình

  • GET /languages/python: Trả về ngôn ngữ Python

DELETE

Xoá một ngôn ngữ lập trình trong danh sách. Ví dụ:

  • DELETE /languages/python: Xoá một ngôn ngữ trong danh sách


Đầu tiên ta viết hàm tạo mới với method = POST:


from flask import Flask

from flask import request

from flask import jsonify

import json

app = Flask(__name__)

languages = []

@app.route("/languages", methods=['POST'])

def create_language():

  body = json.loads(request.data)

  languages.append(body)

  return jsonify(body)


Chúng ta import thư viện để xử lý cho json với 2 module sau:


from flask import jsonify

import json

Trong đó jsonify là thư viện của flask giúp việc trả về dữ liệu dạng format json (đi cùng với content-type) và thư viện json sử dụng để convert dữ liệu do client gửi lên. Thực tế dòng code:   return jsonify(body) 

Chúng ta có thể đổi thành return json.dumps(body)

Tuy nhiên nếu sử dụng hàm này thì kết quả trả về chỉ có dạng json, nhưng content-type là: text/html


Ngoài ra, nếu chúng ta muốn thay đổi status của giao thức http, ta có thể viết như sau:

return jsonify(body),200

Trong trường hợp mặc định, nếu ta chỉ viết:

return jsonify(body)

Thì trạng thái trả về sẽ luôn là 200


Bạn sử dụng Postman để tạo method là POST: điền thông tin json và click SEND

 



Lúc này bạn nhận được kết quả trả về như sau: 



Để lấy ra danh sách hiện tại: Bạn cần add thêm code, lưu ý cần sử dụng jsonify để convert dữ liệu dạng mảng về dạng json:


@app.route("/languages")

def get_languages():

  return jsonify(languages)


Kết quả khi bạn sử dụng trình duyệt hoặc postman như sau: Lưu ý data trả về dạng mảng, ngoài ra bạn có thể nhìn thấy status trả về và content-type:




Tiếp theo, ta thiết lập api lấy ngôn ngữ dạng: GET: languages/{language} để lấy ngôn ngữ lập trình theo key là tên ngôn ngữ


@app.route("/languages/<language>", methods=['GET'])

def get_language_by_key(language):

  result = [item for item in languages if item["language"] == language]

  if len(result) > 0:

   return jsonify(result[0])

  else:

   return jsonify({"status": "not found"}),404


Trường hợp này, ta lấy ngôn ngữ, trường hợp tìm kiếm không trả về kết quả ta sẽ báo lỗi và trả về status = 404


Tiếp theo, ta thử cài đặt method DELETE để xoá bớt ngôn ngữ lập trình:


@app.route("/languages/<language>", methods=['DELETE'])

def delele_language(language):

  global languages

  languages = [item for item in languages if item["language"] != language]

  return jsonify({"status": "ok"})


Lưu ý: Ở đây ta coi mỗi ngôn ngữ là duy nhất, khi xoá ngôn ngữ, ta tìm trong danh sách ngôn ngữ đó, và tạo ra một danh sách mới đã loại trừ ngôn ngữ đó. 


Mời các bạn đọc thêm bài Python ứng dụng: Crawl trang web bằng Scrapy.


- Tech Zone -

Thư giãn chút nào!!!

Bài viết liên quan