• Что бы вступить в ряды "Принятый кодер" Вам нужно:
    Написать 10 полезных сообщений или тем и Получить 10 симпатий.
    Для того кто не хочет терять время,может пожертвовать средства для поддержки сервеса, и вступить в ряды VIP на месяц, дополнительная информация в лс.

  • Пользаватели которые будут спамить, уходят в бан без предупреждения. Спам сообщения определяется администрацией и модератором.

  • Гость, Что бы Вы хотели увидеть на нашем Форуме? Изложить свои идеи и пожелания по улучшению форума Вы можете поделиться с нами здесь. ----> Перейдите сюда
  • Все пользователи не прошедшие проверку электронной почты будут заблокированы. Все вопросы с разблокировкой обращайтесь по адресу электронной почте : info@guardianelinks.com . Не пришло сообщение о проверке или о сбросе также сообщите нам.

Centralize HTTP Error Handling in Go

Lomanu4 Оффлайн

Lomanu4

Команда форума
Администратор
Регистрация
1 Мар 2015
Сообщения
1,481
Баллы
155

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.



In this short post, I'll share with you a simple pattern

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

to centralize error handling for my HTTP handlers.

If you've written any amount of Go HTTP servers, you've probably gotten tired of writing the same error handling code over and over again:


func SomeHandler(w http.ResponseWriter, r *http.Request) {
data, err := fetchSomeData()
if err != nil {
http.Error(w, "Failed to fetch data", http.StatusInternalServerError)
log.Printf("Error fetching data: %v", err)
return
}

// More if-err blocks...
}

This code is repetitive, error-prone, and clutters your handlers with boilerplate instead of business logic.

A Better Way


The core idea is simple: change your handlers to return errors instead of handling them directly.

Step 1: Define Custom HTTP Errors


package httperror

import (
"errors"
"net/http"
)

type HTTPError struct {
error
Code int
}

func New(code int, message string) *HTTPError {
return &HTTPError{
error: errors.New(message),
Code: code,
}
}

func NotFound(message string) *HTTPError {
return New(http.StatusNotFound, message)
}

// Add more helpers as needed...
Step 2: Create a Handler Wrapper


// Define a new handler type that returns an error
type HTTPHandlerWithErr func(http.ResponseWriter, *http.Request) error

// Handle wraps your error-returning handlers
func (r *Router) Handle(pattern string, handler HTTPHandlerWithErr) {
r.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
if err := handler(w, r); err != nil {
// Check if it's an HTTPError
var httpErr *httperror.HTTPError
if errors.As(err, &httpErr) {
http.Error(w, err.Error(), httpErr.Code)
slog.Debug("http error", "code", httpErr.Code, "err", err.Error())
} else {
// Default to 500
http.Error(w, err.Error(), http.StatusInternalServerError)
slog.Error("internal server error", "err", err.Error())
}
}
})
}

This wrapper does all the error handling heavy lifting. It uses errors.As() to check if the error is an HTTPError and extract the status code.

Step 3: Add Method-Specific Helpers


func (r *Router) Get(pattern string, handler HTTPHandlerWithErr) {
r.Handle("GET "+pattern, handler)
}

// Add Post, Put, Patch, Delete methods...
Step 4: Write Clean Handlers


func (c *ContainersController) Show(w http.ResponseWriter, r *http.Request) error {
id := r.PathValue("id")

container, err := c.service.FindContainer(id)
if err != nil {
if errors.Is(err, store.ErrNotFound) {
return httperror.NotFound("container not found")
}
return err
}

return json.NewEncoder(w).Encode(container)
}

Look how clean this handler is now! It focuses on its job instead of error handling gymnastics.

What's Next?


Once you've got this pattern in place, you can:

  1. Use JSON responses: Return errors as JSON for API endpoints
  2. Add request IDs: Thread request IDs through logs and responses
  3. Build error-aware middleware: Create middleware that works with your error-returning handlers
  4. Improve error pages: Replace plain text errors with proper error pages

This pattern works with any router that accepts standard Go handlers. It's a small change that makes a huge difference in code quality and maintainability.


Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

 
Вверх Снизу