Flask - 錯誤處理
目標
- 理解 Flask 的錯誤處理機制
- 自定義錯誤處理器處理 404、400 等錯誤
- 返回一致的 JSON 錯誤響應
步驟
- 準備環境
- 我們將基於第4天的模塊化結構進行修改。確保您的工作目錄是
flask_api/
,並激活虛擬環境:1 2
# Windows: flask_api_env\Scripts\activate # macOS/Linux: source flask_api_env/bin/activate
- 我們將基於第4天的模塊化結構進行修改。確保您的工作目錄是
- 當前問題
- 目前,如果請求無效(例如訪問不存在的項目),API 返回基本的錯誤訊息,但格式不一致。今天我們將統一錯誤響應。
- 添加錯誤處理器
- 修改 app/__init__.py,添加全局錯誤處理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
from flask import Flask, jsonify from .routes.items import items_bp def create_app(): app = Flask(__name__) app.config.from_object('app.config.Config') # 註冊藍圖 app.register_blueprint(items_bp, url_prefix='/api/v1') # 自定義錯誤處理器 @app.errorhandler(404) def not_found(error): return jsonify({ 'error': 'Not Found', 'message': str(error) }), 404 @app.errorhandler(400) def bad_request(error): return jsonify({ 'error': 'Bad Request', 'message': str(error) }), 400 @app.errorhandler(500) def internal_error(error): return jsonify({ 'error': 'Internal Server Error', 'message': 'Something went wrong on our end' }), 500 return app
- 代碼解釋:
@app.errorhandler(404)
:捕獲 404 錯誤並返回自定義響應。- 返回的 JSON 格式一致,包含
error
和message
字段。
- 修改 app/__init__.py,添加全局錯誤處理:
- 改進路由中的錯誤處理
- 更新 app/routes/items.py,使用
abort
拋出錯誤,讓全局錯誤處理器接管:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
from flask import Blueprint, jsonify, request, abort items_bp = Blueprint('items', __name__) # 模擬數據庫 items = [ {'id': 1, 'name': 'Item 1', 'price': 10.0}, {'id': 2, 'name': 'Item 2', 'price': 20.0} ] @items_bp.route('/items', methods=['GET']) def get_items(): max_price = request.args.get('max_price', type=float) if max_price: filtered_items = [item for item in items if item['price'] <= max_price] return jsonify({'items': filtered_items}) return jsonify({'items': items}) @items_bp.route('/items', methods=['POST']) def create_item(): if not request.is_json: abort(400, description='Request must be JSON') data = request.get_json() if 'name' not in data or 'price' not in data: abort(400, description='Missing name or price') new_item = { 'id': len(items) + 1, 'name': data['name'], 'price': data['price'] } items.append(new_item) return jsonify(new_item), 201 @items_bp.route('/items/<int:item_id>', methods=['PUT']) def update_item(item_id): item = next((item for item in items if item['id'] == item_id), None) if not item: abort(404, description='Item not found') if not request.is_json: abort(400, description='Request must be JSON') data = request.get_json() item.update({k: v for k, v in data.items() if k in ['name', 'price']}) return jsonify(item), 200
- 代碼解釋:
abort(400, description='...')
:拋出指定狀態碼和描述的錯誤,由全局處理器捕獲。
- 更新 app/routes/items.py,使用
- 運行應用
- 運行:
1
python run.py
- 運行:
- 測試錯誤處理
- 使用 Postman 測試以下場景:
- GET /api/v1/items/999(不存在的 ID):
- 預期響應:
{"error": "Not Found", "message": "404 Not Found: Item not found"}
- 狀態碼:404
- 預期響應:
- POST /api/v1/items(無 JSON):
- 方法:POST,Body 留空
- 預期響應:
{"error": "Bad Request", "message": "400 Bad Request: Request must be JSON"}
- 狀態碼:400
- POST /api/v1/items(缺少字段):
- Body:
{"name": "Item 3"}
- 預期響應:
{"error": "Bad Request", "message": "400 Bad Request: Missing name or price"}
- 狀態碼:400
- Body:
- GET /api/v1/items/999(不存在的 ID):
- 使用 Postman 測試以下場景:
- 模擬 500 錯誤
- 在
create_item
中故意引入錯誤,測試 500 處理器:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
@items_bp.route('/items', methods=['POST']) def create_item(): if not request.is_json: abort(400, description='Request must be JSON') data = request.get_json() if 'name' not in data or 'price' not in data: abort(400, description='Missing name or price') # 模擬錯誤 raise Exception("Test error") new_item = { 'id': len(items) + 1, 'name': data['name'], 'price': data['price'] } items.append(new_item) return jsonify(new_item), 201
- 測試後移除這行。
- 在
- 作業
- 添加一個自定義錯誤處理器處理 405(方法不允許)錯誤。
- 在
update_item
中檢查價格是否為負數,若是則拋出 400 錯誤。
注意事項
- 確保
abort
的描述訊息清晰,方便調試。 - 500 錯誤通常表示服務器內部問題,應記錄日誌(後續會介紹)。
本文章以 CC BY 4.0 授權