FiberMiddleTechnical

Как работает обработка ошибок в Fiber? Что такое custom error handler?

Возвращайте ошибки из обработчиков через return err или fiber.NewError(status, msg). Кастомный error handler задаётся в fiber.Config{ErrorHandler: fn} и централизованно управляет форматом всех HTTP-ошибок. Используйте errors.As для типизированной обработки.

Обработка ошибок в Fiber

Fiber использует единую точку обработки ошибок — error handler. Любой обработчик или middleware может вернуть ошибку через return err, и Fiber передаст её в зарегистрированный error handler. Это позволяет централизовать логирование, форматирование и коды ответов.

Встроенный механизм

По умолчанию Fiber обрабатывает ошибки типа *fiber.Error и стандартные ошибки Go. Если вернуть обычную ошибку — статус будет 500. Для HTTP-ошибок используйте fiber.NewError(statusCode, message):

app.Get("/users/:id", func(c *fiber.Ctx) error {
	id, err := c.ParamsInt("id")
	if err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "id must be integer")
	}
	if id == 0 {
		return fiber.ErrNotFound // предустановленная ошибка 404
	}
	return c.JSON(fiber.Map{"id": id})
})

Кастомный Error Handler

Кастомный error handler задаётся в fiber.Config при создании приложения. Он получает *fiber.Ctx и error, и должен сформировать финальный ответ клиенту:

package main

import (
	"errors"
	"log"

	"github.com/gofiber/fiber/v2"
)

// Кастомный тип ошибки
type AppError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Details string `json:"details,omitempty"`
}

func (e *AppError) Error() string {
	return e.Message
}

func customErrorHandler(c *fiber.Ctx, err error) error {
	// Определяем HTTP-статус
	code := fiber.StatusInternalServerError
	msg := "internal server error"
	details := ""

	// fiber.Error — HTTP-ошибка с явным статусом
	var fiberErr *fiber.Error
	if errors.As(err, &fiberErr) {
		code = fiberErr.Code
		msg = fiberErr.Message
	}

	// AppError — доменная ошибка приложения
	var appErr *AppError
	if errors.As(err, &appErr) {
		code = appErr.Code
		msg = appErr.Message
		details = appErr.Details
	}

	// Логируем 5xx
	if code >= 500 {
		log.Printf("ERROR [%s %s]: %v", c.Method(), c.Path(), err)
	}

	// Устанавливаем Content-Type явно
	c.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSONCharsetUTF8)

	return c.Status(code).JSON(fiber.Map{
		"error":   msg,
		"details": details,
		"path":    c.Path(),
	})
}

func main() {
	app := fiber.New(fiber.Config{
		ErrorHandler: customErrorHandler,
	})

	app.Get("/ok", func(c *fiber.Ctx) error {
		return c.JSON(fiber.Map{"status": "ok"})
	})

	app.Get("/fail", func(c *fiber.Ctx) error {
		return &AppError{Code: 422, Message: "validation failed", Details: "field 'name' is required"}
	})

	app.Get("/crash", func(c *fiber.Ctx) error {
		return errors.New("unexpected database error")
	})

	log.Fatal(app.Listen(":3000"))
}

Предустановленные ошибки Fiber

fiber.ErrBadRequest          // 400
fiber.ErrUnauthorized        // 401
fiber.ErrForbidden           // 403
fiber.ErrNotFound            // 404
fiber.ErrMethodNotAllowed    // 405
fiber.ErrRequestEntityTooLarge // 413
fiber.ErrUnprocessableEntity // 422
fiber.ErrTooManyRequests     // 429
fiber.ErrInternalServerError // 500
fiber.ErrServiceUnavailable  // 503

Совместная работа с middleware recover

Middleware recover перехватывает паники и конвертирует их в вызов error handler с ошибкой 500. Убедитесь, что recover зарегистрирован первым:

import "github.com/gofiber/fiber/v2/middleware/recover"

app.Use(recover.New(recover.Config{
	EnableStackTrace: true,
}))
// Другие middleware после recover...

Подводные камни

  • Возврат nil после c.JSON: Если в обработчике не вернуть ошибку (return nil), но до этого уже вызвать c.JSON, ответ будет отправлен. Двойной вызов записи в ответ приведёт к ошибке fasthttp. Всегда используйте return c.JSON(...).
  • Error handler не вызывается для app.Use без Next: Если middleware вернул ошибку через return err, но c.Next() не был вызван, error handler всё равно сработает. Но если middleware отправил ответ и вернул nil — error handler не вызовется даже при логической ошибке.
  • errors.As vs прямое приведение: Используйте errors.As вместо type assertion — это корректно работает с цепочками ошибок (wrapping через fmt.Errorf("%w", err)).
  • Статус 200 при ошибке: Если в error handler забыть вызвать c.Status(code) перед JSON — ответ уйдёт со статусом 200, несмотря на тело ошибки.
  • Повторная запись заголовков: После того как ответ начал отправляться (headers committed), нельзя изменить статус. Убедитесь, что error handler вызывается до любой отправки данных.
  • Goroutine и ошибки: Ошибки из горутин, запущенных внутри обработчика, не попадают в error handler автоматически. Их нужно перехватывать вручную через каналы или recover внутри горутины.
  • Паника в error handler: Если в самом error handler произойдёт паника — middleware recover её не поймает (он уже отработал). Пишите error handler максимально оборонительно.

Common mistakes

  • Давать ответ про обработка ошибок в Fiber только на уровне определения, не показывая поведение в реальном приложении.
  • Игнорировать границы ответственности вокруг темы «обработка ошибок в Fiber»: кто отменяет работу, кто владеет ресурсом и где формируется ответ клиенту.
  • Не связывать обработка ошибок в Fiber с observability, тестированием или безопасностью, когда это влияет на продакшен-поведение.

What the interviewer is testing

  • Точно объясняет, что именно делает обработка ошибок в Fiber и где это используется в Go-коде.
  • Связывает обработка ошибок в Fiber с корректным lifecycle запроса, отменой, конкурентностью или конфигурацией сервера там, где это уместно.
  • Не изобретает API и опирается на реальные контракты официальной документации.

Sources

Related topics