Skip to content

Hiển thị lỗi

Framework có sẵn một điểm xử lý lỗi chung: phân loại, chọn kiểu hiển thị, chống lặp thông báo, và đưa về trang đăng nhập khi hết phiên. Trang này nói rõ nó làm gì tự động và khi nào bạn cần can thiệp.

Mặc định: không phải làm gì cả

Plugin 04.error của layer gắn sẵn:

  • errorHandler của Vue và hook vue:error — lưới an toàn cho lỗi chưa ai bắt.
  • Hàm $tascoError để trang gọi khi muốn tự quyết định chỗ bắt lỗi.

Quy tắc hiển thị:

Loại lỗiNgười dùng thấy
401Chuyển về /auth/login?redirect=… (không chuyển nếu đang ở trang đăng nhập)
422Không có thông báo — form tự hiện lỗi từng field
5xx, mất mạngnotification góc màn hình, tiêu đề "Lỗi hệ thống"
Còn lại (400, 403, 404…)message ngắn giữa màn hình

Thông báo giống hệt nhau trong vòng 3 giây chỉ hiện một lần — nhiều request cùng hỏng sẽ không phủ kín màn hình.

Tự bắt khi cần

ts
import { message } from '@tasco/ui'

const { $tascoError } = useNuxtApp()

async function remove(id: string) {
  try {
    await removeOrder(id)
    message.success('Đã xoá đơn hàng.')
    await reload()
  } catch (caught) {
    $tascoError(caught)   // vẫn hiển thị theo quy tắc chung
  }
}

Dùng khi cần chạy thêm việc gì đó quanh lỗi (tắt loading, khôi phục trạng thái, ghi log), nhưng vẫn muốn giao diện báo lỗi thống nhất.

Tuỳ chọn

ts
$tascoError(caught, { silent: true })                     // chỉ phân loại, không hiện gì
$tascoError(caught, { fallbackMessage: 'Không xoá được đơn hàng.' })  // khi lỗi không có message

$tascoError trả về AppError đã chuẩn hoá nên dùng tiếp được:

ts
const error = $tascoError(caught, { silent: true })
if (error.status === 409) {
  await confirmOverwrite()
}

Tự dựng giao diện báo lỗi

Handler chung phù hợp cho thao tác ngắn (lưu, xoá). Với lỗi tải dữ liệu của cả màn hình, hiện trạng thái ngay trong trang thay vì bắn toast rồi để trang trống:

vue
<script setup lang="ts">
const { order, loading, error, load } = useOrderDetail(String(route.params.id))
</script>

<template>
  <CCard v-if="loading">Đang tải…</CCard>

  <CEmpty
    v-else-if="error"
    :description="error.message"
    bordered
  >
    <CButton variant="primary" @click="load">Thử lại</CButton>
  </CEmpty>

  <CCard v-else>…</CCard>
</template>

Trang lỗi toàn màn hình

app/error.vue của layer xử lý lỗi fatal (403 từ middleware quyền, 404 route không khớp, lỗi khi dựng app): hiện mã lỗi, thông báo và nút về trang chủ.

Dự án muốn giao diện riêng thì tạo app/error.vue của mình:

vue
<script setup lang="ts">
import type { NuxtError } from '#app'

const props = defineProps<{ error: NuxtError }>()
const isForbidden = computed(() => Number(props.error?.statusCode) === 403)
</script>

<template>
  <div class="error-page">
    <h1>{{ isForbidden ? 'Không có quyền truy cập' : 'Đã xảy ra lỗi' }}</h1>
    <p>{{ error.message }}</p>
    <CButton variant="primary" @click="clearError({ redirect: '/' })">Về trang chủ</CButton>
  </div>
</template>

Trong error.vue, lỗi là NuxtError nên mã nằm ở statusCode — khác với AppError ở phía client dùng status.

Lỗi khi render một trang

Layout mặc định bọc nội dung trang trong <NuxtErrorBoundary>: một trang lỗi lúc render chỉ làm vùng nội dung hiện thông báo, khung app và menu vẫn dùng được. Không cần tự bọc lại, trừ khi muốn khoanh vùng nhỏ hơn:

vue
<NuxtErrorBoundary>
  <BieuDoDoanhThu :data="data" />
  <template #error="{ error }">
    <CEmpty :description="`Không vẽ được biểu đồ: ${error.message}`" bordered />
  </template>
</NuxtErrorBoundary>

Viết thông báo lỗi

  • Tiếng Việt, đủ câu, nói việc gì hỏnglàm gì tiếp: "Không lưu được đơn hàng. Kiểm tra kết nối rồi thử lại."
  • Không hiện mã kỹ thuật, tên hàm, chuỗi stack cho người dùng.
  • Không bao giờ đưa token, header hay dữ liệu nhạy cảm vào thông báo.
  • Thông báo từ backend (statusMessage, message) được hiện thẳng — thống nhất với backend rằng những chuỗi đó viết cho người dùng cuối đọc.

Ghi log

Runtime không dùng console.log. Ở route Nitro, cần ghi thì dùng console.warn / console.error kèm tiền tố tên dự án, và không kèm dữ liệu nhạy cảm:

ts
console.error('[orders] Không gọi được dịch vụ tồn kho:', error.message)

Liên quan