➡️Xử lý yêu cầu (Request parsing)
Chương này trình bày cách sử dụng module reqparse
để xử lý (parse) đối số/danh sách đối số đầu vào (arguments) của một yêu cầu (request). Để thuận tiện trong việc trình bày, chúng tôi sẽ sử dụng một số thuật ngữ tiếng Anh sau: parse (xử lý), argument (đối số), request (yêu cầu).
Làm quen với module reqparse
reqparse
Đoạn mã nguồn bên dưới trình bày một ví dụ cách khai báo và sử dụng module reqparse
để trích xuất hai đối số là rate
kiểu int
, và name
kiểu str
. Trong module reqparse
, trường hợp không chỉ định kiểu dữ liệu, thì mặc định là kiểu dữ liệu chuỗi - str
. Khai báo help
trong đoạn mã nguồn được dùng để thông báo lỗi khi phát sinh lỗi sai kiểu dữ liệu.
from flask_restful import reqparse
parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, help='Rate cannot be converted')
parser.add_argument('name')
args = parser.parse_args()
Đối số bắt buộc
Đối số bắt buộc (Require arguments) là kỹ thuật buộc một request phải bao gồm giá trị (value) cho một thuộc tính được chỉ định. Điều này được thực hiện qua khai báo required=True
.
parser.add_argument('name', required=True, help="Name is a required argument!")
Đối số đa trị
Đối số đa trị (Multiple values) là kỹ thuật cho phép một tham số được truyền vào nhiều giá trị. Điều này được thực hiện qua khai báo action='append'
. Kết quả sau khi xử lý (parse) là một mảng chứa các giá trị được truyền vào.
parser.add_argument('name', action='append')
Khai báo này cho phép một yêu cầu (request) có cấu trúc như sau:
$ curl http://127.0.0.1:5000/ -d "name=anna" -d "name=bob" -d "name=chris"
Kết quả sau khi xử lý (parse) có dạng như sau:
args = parser.parse_args()
args['name'] # ['anna', 'bob', 'chris']
Đổi tên đối số sau khi xử lý
Đổi tên đối số sau khi xử lý, được dịch từ thuật ngữ "Other Destinations", là kỹ thuật cho phép thay đổi tên của đối số sau khi xử lý (parse), được chỉ thị qua khai báo dest='new_name'
.
parser.add_argument('name', dest='public_name')
args = parser.parse_args()
args['public_name']
Xử lý đối số theo trường dữ liệu
Argument Locations - Xử lý đối số theo trường dữ liệu là kỹ thuật cho phép xử lý đối số đầu vào từ nhiều trường dữ liệu khác nhau, thông qua khai báo location='...'
, chẳng hạn, User-Agent
, session_id
. Một cách tổng quát, tất cả các trường dữ liệu được hỗ trợ bởi flask.Request
đều có thể được sử dụng trong phương thức add_argument()
.
# Look only in the POST body
parser.add_argument('name', type=int, location='form')
# Look only in the querystring
parser.add_argument('PageSize', type=int, location='args')
# From the request headers
parser.add_argument('User-Agent', location='headers')
# From http cookies
parser.add_argument('session_id', location='cookies')
# From file uploads
parser.add_argument('picture', type=werkzeug.datastructures.FileStorage, location='files')
Đoạn mã nguồn trên trình bày một số trường dữ liệu mà một đối tượng parser
có thể truy cập. Lưu ý rằng, nếu location='json'
thì phải sử dụng kèm với kiểu dữ liệu type=list
.
Khai báo location
trong phương thức add_argument()
còn có thể nhận dữ liệu dạng mảng để xử lý từ nhiều trường dữ liệu khác nhau, được gọi là xử lý đa trường dữ liệu (Multiple Locations), như ví dụ bên dưới.
parser.add_argument('text', location=['headers', 'values'])
Danh sách các đối số, nếu sử dụng kỹ thuật Multiple Locations, sẽ được lưu trữ theo cấu trúc dữ liệu MultiDict
được định nghĩa trong lớp werkzeug.datastructures.MultiDict
.
Kế thừa Parser (Parser Inheritance)
🤖 Thường thì bạn sẽ định nghĩa các parser khác nhau cho mỗi tài nguyên (resource). Vấn đề của điều này là nếu các parser chia sẻ các đối số chung, như vậy, thay vì viết lại các parser, chúng ta có thể viết một parser chung (parent parser) chứa tất cả các đối số chung và sau đó mở rộng (extend) parser đó với phương thức copy()
. Chúng ta cũng có thể ghi đè lên bất kỳ đối số nào trong parser chung bằng cách sử dụng phương thức replace_argument()
, hoặc loại bỏ nó hoàn toàn với phương thức remove_argument()
.
from flask_restful import reqparse
# Parent parser
parser = reqparse.RequestParser()
parser.add_argument('foo', type=int)
# Extended parser
# parser_copy has both 'foo' and 'bar'
parser_copy = parser.copy()
parser_copy.add_argument('bar', type=int)
parser_copy.replace_argument('foo', required=True, location='json')
# 'foo' is now a required str located in json,
# not an int as defined by the original parser
# parser_copy no longer has 'foo' argument
parser_copy.remove_argument('foo')
Kiểm soát lỗi (Error Handling)
🤖 Cách mặc định để RequestParser
xử lý lỗi là tự động dừng lại khi gặp lỗi đầu tiên. Điều này có thể hữu ích khi có nhiều đối số, dẫn đến việc có thể mất thời gian để xử lý. Tuy nhiên, việc gom nhóm các lỗi lại và gửi tất cả về client cùng một lúc được xem là giải pháp tốt hơn. Tính nắng này có thể được chỉ định ở cấp độ ứng dụng Flask hoặc trên các parser cụ thể. Để sử dụng RequestParser
với tùy chọn gom lỗi, bạn có thể truyền đối số bundle_errors
.
from flask_restful import reqparse
parser = reqparse.RequestParser(bundle_errors=True)
parser.add_argument('foo', type=int, required=True)
parser.add_argument('bar', type=int, required=True)
# If a request comes in not containing both 'foo' and 'bar',
# the error that will come back will look something like this.
#{
# "message": {
# "foo": "foo error message",
# "bar": "bar error message"
# }
#}
# The default behavior would only return the first error
parser = RequestParser()
parser.add_argument('foo', type=int, required=True)
parser.add_argument('bar', type=int, required=True)
#{
# "message": {
# "foo": "foo error message"
# }
#}
Tham số BUNDLE_ERRORS
có thể được cấu hình ở cấp độ ứng dụng Flask, bằng cách khai báo như sau:
from flask import Flask
app = Flask(__name__)
app.config['BUNDLE_ERRORS'] = True
Bên cạnh tính năng gom lỗi (bundle errors), các parser của module reqparse
còn hỗ trợ gửi thông báo lỗi thông qua tham số help
, bằng cách kết hợp thêm với chỉ thị {error_msg}
.
from flask_restful import reqparse
parser = reqparse.RequestParser()
parser.add_argument(
'foo',
choices=('one', 'two'),
help='Bad choice: {error_msg}'
)
# If a request comes in with a value of "three" for 'foo':
#{
# "message": {
# "foo": "Bad choice: three is not a valid choice",
# }
#}
Tổng kết
Chương này đã trình bày các kỹ thuật xử lý đối số đầu vào của một yêu cầu - request parsing - thông qua module reqparse
. Chương tiếp theo sẽ trình bày các kỹ thuật xử lý phản hồi (response) cho mỗi yêu cầu (request) từ client.
Last updated