Khi người dùng mở một bài viết không tồn tại, hoặc vào trang quản trị khi chưa có quyền, view phải dừng ngay và trả đúng mã lỗi — không phải tự render template lỗi rồi trả về với status 200. Flask có sẵn abort(): nó ném một ngoại lệ HTTP, dừng view ngay tại chỗ, và Flask tự trả đúng status code kèm trang lỗi. Kết hợp với errorhandler() là bạn có trang lỗi đẹp, đúng chuẩn cho toàn bộ ứng dụng.
Vấn đề
Cách hay gặp nhất là tự trả chuỗi lỗi kèm mã trạng thái: return "Không tìm thấy bài viết", 404. Cách này chạy được, nhưng mỗi view phải lặp lại cùng một mẫu, và rất dễ quên mã trạng thái — chỉ cần viết thiếu , 404 là trang lỗi trả về 200, Google sẽ index cả trang “không tìm thấy”, còn client hay API thì không biết request đã thất bại. Tệ hơn là render template lỗi rồi return bình thường: trang nhìn đúng, nhưng status code vẫn là 200.
Code lỗi: tự trả chuỗi lỗi, dễ quên mã trạng thái
# Cách kém: dễ quên mã lỗi, mỗi view lặp lại một kiểu@app.get("/bai-viet/<int:post_id>")def xem_bai_viet(post_id): post = Post.query.get(post_id) if post is None: return "Không tìm thấy bài viết", 404 # quên ", 404" là thành 200 return render_template("post.html", post=post)
Giải pháp
abort() nhận mã lỗi HTTP và dừng view ngay lập tức — mọi dòng code phía sau không chạy nữa. Muốn nhắn thêm lý do thì truyền description, Flask sẽ đưa nó vào trang lỗi mặc định.
Code đúng: abort() dừng view ngay, kèm description
from flask import Flask, abort, render_templateapp = Flask(__name__)@app.get("/bai-viet/<int:post_id>")def xem_bai_viet(post_id): post = Post.query.get(post_id) if post is None: abort(404, description=f"Không có bài viết #{post_id}") if post.is_private and not current_user.is_admin: abort(403, description="Bài viết này chỉ dành cho quản trị viên") return render_template("post.html", post=post)
Trang lỗi mặc định của Flask khá thô, nên đăng ký errorhandler() một lần cho toàn app để render template đẹp của riêng bạn — đối tượng lỗi có sẵn code và description để hiển thị:
Code đúng: errorhandler() cho trang lỗi thống nhất
@app.errorhandler(404)def page_not_found(error): return render_template("404.html", message=error.description), 404@app.errorhandler(403)def forbidden(error): return render_template("403.html", message=error.description), 403
Một lưu ý quan trọng: abort() chỉ có ý nghĩa trong request context, tức là trong view, trong before_request, hoặc trong hàm helper được view gọi. Ngoài request — ví dụ trong tác vụ nền hay Flask shell — nó vẫn ném ra ngoại lệ HTTP, nhưng không có request nào để Flask chuyển ngoại lệ đó thành response, nên nó chỉ là một exception chưa được xử lý.
Áp dụng vào dự án của bạn
Quy tắc chọn mã nhanh: không tìm thấy tài nguyên thì abort(404), không có quyền truy cập thì abort(403), dữ liệu đầu vào sai định dạng thì abort(400). Viết description ngắn gọn, hướng về người dùng (“Không có bài viết #12”) thay vì lộ chi tiết nội bộ. Và đăng ký errorhandler() cho 404, 403 ngay từ đầu dự án — một lần duy nhất, mọi abort() trong app đều được hưởng trang lỗi thống nhất.
Bài viết liên quan
- Flask: dùng Application Factory (create_app) để app dễ test, dễ mở rộng ngay từ đầu
- Flask: tổ chức route gọn gàng bằng Blueprint khi dự án lớn dần
- Flask: quản lý config theo môi trường (dev/staging/prod), không còn hardcode SECRET_KEY
Câu hỏi thường gặp
abort() khác gì return (“…”, 404)?
abort() ném ngoại lệ nên view dừng ngay tại chỗ, code phía sau không chạy. return kèm tuple chỉ trả response sau khi view chạy hết — nếu quên mã trạng thái thì response thành 200, và bạn phải lặp lại mẫu này ở mọi view.
description có hiển thị cho người dùng không?
Có, nếu trang lỗi của bạn dùng nó. Trang lỗi mặc định của Flask đã hiện description; với errorhandler() tự viết, lấy qua error.description rồi đưa vào template như ví dụ trên.
abort() dùng được ngoài view không?
Không nên. Ngoài request context, abort() vẫn ném ra ngoại lệ HTTP (ví dụ NotFound), nhưng không có request nào để Flask chuyển ngoại lệ đó thành response — trong tác vụ nền hay Flask shell nó chỉ là một exception chưa được xử lý. Chỉ dùng abort() trong view, before_request, hoặc hàm helper được view gọi.
API thì xử lý lỗi thế nào?
Đăng ký errorhandler() trả JSON thay vì HTML, ví dụ {"error": error.description} kèm error.code. Client nhận đúng mã lỗi và thông báo, thay vì phải parse trang HTML.
💬 Bạn đã thử cách này trong dự án của mình chưa? Nếu gặp lỗi hoặc có cách làm gọn hơn, để lại bình luận bên dưới — mình đọc hết và sẽ bổ sung những ý hay vào bài.
Nguồn tham khảo
- Flask documentation, Application Errors: https://flask.palletsprojects.com/en/stable/errorhandling/
- Flask documentation, API — abort(): https://flask.palletsprojects.com/en/stable/api/#flask.abort
