بهترین روش ها برای ساخت یک لایه اعتبار سنجی در Go

اعتبار سنجی بخشی ضروری از هر سیستم نرم افزاری است. این کمک می کند تا اطمینان حاصل شود که داده های پردازش یا ذخیره شده در سیستم صحیح هستند و محدودیت های مورد نیاز را برآورده می کنند.
در این مقاله، نحوه پیادهسازی یک لایه اعتبارسنجی عمومی در Go را مورد بحث قرار میدهیم که میتواند برای اعتبارسنجی هر شی با استفاده از لیستی از اعتبارسنجیها استفاده شود.
مشکل
سناریویی را در نظر بگیرید که در آن ما یک سیستم ثبت نام کاربر داریم که جزئیات کاربر مانند نام، ایمیل و رمز عبور را می پذیرد. قبل از ذخیره داده های کاربر در پایگاه داده، باید آن را اعتبار سنجی کنیم تا اطمینان حاصل کنیم که داده ها با محدودیت های مورد نیاز مطابقت دارند. به عنوان مثال، نام نباید خالی باشد، ایمیل باید در قالب معتبر باشد و رمز عبور باید حداقل الزامات قدرت را داشته باشد.
یکی از راههای پیادهسازی لایه اعتبارسنجی، تعریف یک تابع اعتبارسنجی برای هر محدودیت و فراخوانی یک به یک برای هر شی است. با این حال، این رویکرد می تواند خسته کننده و مستعد خطا باشد، به خصوص زمانی که با تعداد زیادی از اشیا سروکار دارید.
راه حل
برای حل این مشکل، میتوانیم یک لایه اعتبارسنجی عمومی را پیادهسازی کنیم که فهرستی از اعتبارسنجیها را میگیرد و آنها را برای هر شی اعمال میکند.
package validation
import (
"errors"
"fmt"
)
type Rule func(key string, value interface{}) error
type Rules []Rule
type Validator struct {
rules Rules
}
func (v *Validator) Add(rule Rule) {
v.rules = append(v.rules, rule)
}
func (v *Validator) Validate(data map[string]interface{}) []error {
var errors []error
for _, rule := range v.rules {
for key, value := range data {
if err := rule(key, value); err != nil {
errors = append(errors, err)
}
}
}
return errors
}
func ValidateLength(maxLength int) Rule {
return func(key string, value interface{}) error {
str, ok := value.(string)
if !ok {
return fmt.Errorf("%s is not a string", key)
}
if len(str) > maxLength {
return fmt.Errorf("%s must be less than or equal to %d characters", key, maxLength)
}
return nil
}
}
func ValidatePresence(key string, value interface{}) error {
if value == "" {
return fmt.Errorf("%s can't be blank", key)
}
return nil
}
func ValidateRange(min, max int) Rule {
return func(key string, value interface{}) error {
num, ok := value.(int)
if !ok {
return fmt.Errorf("%s is not a number", key)
}
if num < min || num > max {
return fmt.Errorf("%s must be between %d and %d", key, min, max)
}
return nil
}
}
func ValidateEmail(key string, value interface{}) error {
str, ok := value.(string)
if !ok {
return fmt.Errorf("%s is not a string", key)
}
// simplified email validation for example purposes
if str == "" || str[:1] == "@" || str[len(str)-1:] == "@" {
return fmt.Errorf("%s is not a valid email address", key)
}
return nil
}
نوع Rule یک قانون اعتبارسنجی واحد را نشان میدهد که یک جفت کلید-مقدار را میگیرد و اگر مقدار با معیارهای قانون مطابقت نداشته باشد، یک خطا برمیگرداند.
نوع Rules برشی از انواع Rule است که مجموعه ای از قوانین اعتبار سنجی را نشان می دهد.
نوع Validator شامل لیستی از قوانین است و دارای روش هایی برای افزودن قوانین جدید و اعتبارسنجی داده ها در برابر تمام قوانین موجود در لیست است.
متد Add به شما امکان می دهد یک قانون اعتبار سنجی جدید به اعتبار سنجی اضافه کنید.
متد Validate نقشه ای از جفت های کلید-مقدار را می گیرد و لیستی از خطاهای یافت شده در طول فرآیند اعتبار سنجی را برمی گرداند.
توابع ValidateLength، ValidatePresence، ValidateRange و ValidateEmail نمونه هایی از قوانین از پیش تعریف شده هستند که می توانید از آنها استفاده کنید. این توابع یک نوع Rule را برمیگردانند که میتوانید با استفاده از متد Add به یک نمونه Validator اضافه کنید.
اکنون که لایه اعتبار سنجی خود را آماده کرده ایم، بیایید ببینیم چگونه می توانیم از آن برای تأیید اعتبار بدنه درخواست یک نقطه پایانی API استفاده کنیم. ما یک کنترلکننده ساده HTTP ایجاد میکنیم که بدنه درخواست JSON را میپذیرد و آن را در برابر مجموعهای از قوانین اعتبارسنجی تأیید میکند.
func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
var user User
err := json.NewDecoder(r.Body).Decode(&user)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "failed to decode request body: %v", err)
return
}
// Define validation rules
rules := []validation.Rule{
validation.ValidateRequired("Name", user.Name),
validation.ValidateLength("Name", user.Name, 3, 50),
validation.ValidateEmail("Email", user.Email),
validation.ValidateRequired("Password", user.Password),
validation.ValidateLength("Password", user.Password, 8, 50),
}
// Execute validation rules and get errors
errors := validation.Execute(rules)
if len(errors) > 0 {
w.WriteHeader(http.StatusBadRequest)
for _, err := range errors {
fmt.Fprintf(w, "%s\n", err.Error())
}
return
}
// Do something with the validated user object
// ...
}
در اینجا، قوانین اعتبارسنجی را به عنوان تکهای از اشیاء اعتبارسنجی تعریف میکنیم. سپس این برش را به تابع validation.Execute میدهیم، که قوانین اعتبارسنجی را اجرا میکند و تکهای از خطاها را برمیگرداند.
اگر هر گونه خطای اعتبار سنجی رخ دهد، ما یک کد وضعیت 400 Bad Request را با لیست خطاها برمی گردانیم. در غیر این صورت، ما کاری را با شی کاربر معتبر انجام می دهیم.
نتیجه
در این مقاله، نحوه ایجاد یک لایه اعتبار سنجی انعطاف پذیر و قابل استفاده مجدد در Go را با استفاده از گزینه های کاربردی و بسته شدن دیدیم. این رویکرد به ما اجازه می دهد تا قوانین اعتبار سنجی پیچیده را تعریف کنیم و از آنها در بخش های مختلف برنامه خود استفاده مجدد کنیم.
با جدا کردن منطق اعتبارسنجی از منطق تجاری برنامه ما، میتوانیم کد خود را ماژولارتر و آزمایش آن آسانتر کنیم. همچنین میتوانیم در صورت بروز خطاهای اعتبارسنجی، بازخورد بهتری به کاربر ارائه دهیم.
امیدوارم این مقاله مفید بوده باشد و نقطه شروع خوبی برای ایجاد لایه اعتبار سنجی خود در Go در اختیار شما قرار دهد.