Привет, Хабровчане! Сегодня я хочу рассказать о своём небольшом проекте — пакете goform, который я написал для упрощения работы с HTML-формами в Go. Это не просто ещё один пакет, а результат моего опыта и желания сделать процесс работы с формами более удобным и эффективным. В этой статье я поделюсь историей создания, функциональностью пакета и тем, как он может быть полезен другим разработчикам.
Заранее хочу сказать что статья никоим образом не предназначена для пиара, понимаю что задачка фактически не очень сложная и возможно кто-то уже сделал что-то похожее для себя или организации в которой работает. А так же я не претендую на звание «мастера кода по всем видам кода».
Как всё начиналось
Идея создания goform появилась во время работы над одним из моих пет-проектов. Я заметил, что постоянно приходится писать однотипный код для создания, рендеринга и валидации форм. Это было достаточно утомительно потому как в каждой форме приходилось руками подставлять значения. Мне захотелось создать инструмент, который бы автоматизировал эти процессы и позволил сосредоточиться на логике приложения, а не на рутинных задачах.
Так и родилась идея goform — пакета, который предоставляет удобный API для работы с HTML-формами в Go. Основная цель — сделать процесс создания и обработки форм максимально простым и гибким.
Какой у goform функционал на данный момент?
goform — это не просто обёртка для работы с формами. Это инструмент, который решает несколько ключевых задач:
-
Упрощение создания форм: Вместо того чтобы вручную прописывать каждое поле и его атрибуты, вы можете описать форму с помощью структуры Go, и GoForm сделает всё остальное.
-
Гибкость рендеринга: Пакет поддерживает рендеринг как в HTML, так и в JSON, что делает его универсальным для монолитных приложений и разделённых фронтенда и бэкенда.
-
Валидация данных: Пакет предоставляет встроенные правила валидации и позволяет добавлять кастомные.
-
Поддержка AJAX: Пакет автоматически определяет AJAX-запросы и возвращает данные в формате JSON.
-
Интеграция с фреймворками: goform легко интегрируется с фреймворком — Echo (просто потому что в своей практике написания веб-приложений на Go, я применяю его в 99% случаев).
Об остальном функционале сможете прочитать подробнее в документации пакета (ссылки предоставлю в конце статьи).
Основные функции пакета
1. Создание формы
Для создания формы достаточно описать её структуру и вызвать функцию NewForm:
Скрытый текст
// Инициализируем структуру (поля будущей формы). type RegistrationForm struct { Username string `form:"username" validate:"required,min=3"` Email string `form:"email" validate:"required,email"` Password string `form:"password" validate:"required,min=6"` Method string `form:"-"` FormID string `form:"-"` } func main() { http.HandleFunc("/someurl", func(w http.ResponseWriter, r *http.Request) { model := &RegistrationForm{ Method: "SomeMethod", FormID: "SomeFormID", } /* model - структура, описывающая поля формы. Method - HTTP-метод формы (например, "POST"). FormID - уникальный идентификатор формы. */ form := core.NewForm(model, model.Method, model.FormID) // Остальной код }) }
2. Рендеринг формы
GoForm поддерживает рендеринг как в HTML, так и в JSON. Это особенно полезно, если фронтенд и бэкенд разделены:
Скрытый текст
response := form.ToResponse() if form.RenderHTML { // Рендеринг HTML _ = tmpl.ExecuteTemplate(w, "имя_шаблона.html", response) } else { // Возврат JSON w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response) }
3. Валидация данных
Пакет предоставляет встроенные правила валидации, такие как required, min, max, email, и позволяет добавлять кастомные:
Скрытый текст
form.AddCustomValidation("password", func(value string) error { if len(value) < 6 { return errors.New("password must be at least 6 characters long") } return nil })
4. Обработка AJAX-запросов
Пакет автоматически определяет AJAX-запросы и возвращает данные в формате JSON:
Скрытый текст
if isAjax(r) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(form.ToResponse()) }
5. Поддержка CSRF-токенов
Пакет поддерживает генерацию и проверку CSRF-токенов для защиты от атак:
Скрытый текст
token, err := core.GenerateCSRFToken() if err != nil { http.Error(w, "Failed to generate CSRF token", http.StatusInternalServerError) } form.AddCSRFToken(token)
С чем я столкнулся во время разработки?
Разработка пакета не обошлась без сложностей. Вот основные проблемы, с которыми я столкнулся:
-
Гибкость рендеринга: Одна из главных задач — сделать пакет универсальным. Я хотел, чтобы он мог работать как с монолитными приложениями, так и с разделёнными фронтендом и бэкендом. Это потребовало реализации поддержки как HTML, так и JSON.
-
Валидация данных: Реализация гибкой системы валидации, которая поддерживает как встроенные правила, так и кастомные, оказалась нетривиальной задачей. Пришлось продумать, как эффективно обрабатывать ошибки и возвращать их пользователю.
-
Интеграция с фреймворком: Чтобы goform был полезен во-первых мне, а во-вторых максимальному числу разработчиков, я добавил поддержку фреймворка Echo. Это потребовало некоторого переосмысления, но результат того стоил.
Как goform может быть полезен?
goform — это не просто инструмент для работы с формами. Это решение, которое может сэкономить вам время и усилия. Вот несколько сценариев, где он может быть полезен:
-
Монолитные приложения: Если вы разрабатываете монолитное приложение, GoForm упростит создание и обработку форм.
-
Разделённые фронтенд и бэкенд: Пакет поддерживает рендеринг в JSON, что делает его идеальным для REST API.
-
Быстрое прототипирование: С goform вы можете быстро создавать и тестировать формы, не тратя время на рутинные задачи.
Пример использования
Вот пример простой формы регистрации:
Скрытый текст
package main import ( "net/http" "github.com/DBenyukh/goform/core" "html/template" "path/filepath" "log" ) type RegistrationForm struct { Username string `form:"username" validate:"required,min=3"` Email string `form:"email" validate:"required,email"` Password string `form:"password" validate:"required,min=6"` Method string `form:"-"` FormID string `form:"-"` } var tmpl *template.Template func init() { projectDir, err := filepath.Abs(".") if err != nil { log.Fatalf("Error getting absolute project directory path: %v", err) } templateDir := filepath.Join(projectDir, "templates") renderer, err := core.NewTemplateRenderer(templateDir, "") if err != nil { log.Fatalf("Failed to create template renderer: %v", err) } tmpl = renderer.Templates } func main() { http.HandleFunc("/register", func(w http.ResponseWriter, r *http.Request) { model := &RegistrationForm{ Method: "POST", FormID: "register_form", } form := core.NewForm(model, model.Method, model.FormID) form.RenderHTML = true if r.Method == http.MethodGet { // Рендеринг формы response := form.ToResponse() if form.RenderHTML { // Рендеринг HTML _ = tmpl.ExecuteTemplate(w, "имя_шаблона.html", response) } else { // Возврат JSON w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response) } return } // Обработка POST-запроса if err := form.Bind(r); err != nil { http.Error(w, "Invalid form data", http.StatusBadRequest) return } if err := form.Validate(model); err != nil { // Возврат ошибок валидации response := form.ToResponse() if form.RenderHTML { _ = tmpl.ExecuteTemplate(w, "имя_шаблона.html", response) } else { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response) } return } // Обработка успешной отправки формы w.Write([]byte("User registered successfully!")) }) http.ListenAndServe(":8080", nil) }
И соответственно html-шаблон который использую:
Скрытый текст
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <form method="{{ if eq .Method "GET" }}GET{{ else }}POST{{ end }}"> {{ if and (ne .Method "GET") (ne .Method "POST") }} <input type="hidden" name="_method" value="{{ .Method }}"> {{ end }} <input type="hidden" name="form_id" value="{{ .FormID }}"> {{ range .Fields }} {{ if not .Hidden }} <div> <label>{{ .Name }}</label> <input type="{{ .Type }}" name="{{ $.FormID }}_{{ .Name }}" value="{{ .Value }}"> {{ if .Error }} <span style="color: red;">{{ .Error }}</span> {{ end }} </div> {{ end }} {{ end }} <input type="hidden" name="{{ .FormID }}_csrf_token" value="{{ .CSRF }}"> <button type="submit">Submit</button> </form> </body> </html>
Заключение
goform — это результат моего желания сделать работу с формами в Go более удобной и эффективной. Пакет уже используется в моём проекте (над которым пока работаю), и я надеюсь, что он будет полезен и другим разработчикам. Если у вас есть идеи или предложения по улучшению, буду рад услышать их в комментариях / личке на хабре / ТГ (в ссылках). А так же буду рад всевозможной критике.
И конечно же ссылочки, куда без них:
Мой телеграм (если вдруг захотите просто пообщаться или предложить работу)
Спасибо за внимание! Удачи в ваших проектах!
ссылка на оригинал статьи https://habr.com/ru/articles/888728/
Добавить комментарий