ساخت تأیید کننده ایمیل با Go
اولین مرحله از ارسال سرد با دریافت آدرس ایمیل استخدام کننده فنی شروع می شود. اغلب از طریق اینترنت گفته می شود که به طور تصادفی ایمیل با ترکیب نام و نام خانوادگی استخدام کننده تشکیل دهید.
بگویید، اگر می خواهید به من ایمیل بزنید، ترکیب های ممکن می تواند باشد aniket.pal@companymail.com
، pal.aniket@companymail.com
، paniket@companymail.com
، aniketpal@comapanymail.com
و مرتبط ایجاد یک لیست عظیم از جایگشت و ترکیب های متعدد. برای کوتاه کردن فضای جستجو، یک برنامه کاربردی ایجاد خواهیم کرد تا بررسی کنیم که آیا companymail.com معتبر است یا نه.
هدف از آموزش این است که به شما درک کاملی از قابلیت های go-lang بدهد. ما از هیچ ماژول شخص ثالثی استفاده نخواهیم کرد، فقط با ماژول های اصلی Go که خواهیم ساخت.
چرا Go را انتخاب کنید؟ 🤨
داستان از این قرار است که توسعه دهندگان گوگل Golang را توسعه دادند در حالی که منتظر بودند تا زبان های دیگر کامپایل شوند. توسعه دهندگان گوگل به دلیل نارضایتی خود از مجموعه ابزار خود، مجبور شدند به طور کامل درباره توسعه سیستم تجدید نظر کنند، که آنها را به ایجاد یک راه حل ناب، متوسط و کامپایل سوق داد که از چند رشته ای عظیم، همزمانی و عملکرد تحت استرس پشتیبانی می کند.
هر سازمانی که به مقیاس نگاه می کند، از Golang برای ساخت ریزسرویس های کانتینری استفاده می کند.
علاوه بر این، Go یک زبان عالی برای ایجاد وب سرورها و خدمات وب ساده و در عین حال کارآمد است. این یک بسته HTTP داخلی را ارائه می دهد که شامل ابزارهایی برای ایجاد سریع وب یا سرور فایل است.
همچنین، اگر به اکوسیستم Cloud Native علاقه دارید، Go زبانی است که باید با آن شروع کنید. در این مورد، شما هرگز یک سرور باطن را با Go checkout ایجاد نکرده اید.
چه چیزی خواهیم ساخت؟ 👨🚒
ابزار کوچک بررسی می کند که آیا دامنه ایمیل وجود دارد یا خیر. هدف این است که بفهمیم چگونه می توان با استفاده از Go Lang چگونه بک اند و فرانت اند ساخت. ما با سرور باطن شروع می کنیم و سپس به ساختن قسمت جلویی می رویم و در عین حال فضایی برای کاوش در اختیار شما قرار می دهیم.
ساخت Backend 🚪
ما از قسمت جلویی برنامه برای دریافت دامنه مورد نیاز خود استفاده می کنیم.
تعریف پکیج اصلی و وارد کردن بسته های مورد نیاز.
package main
import (
"encoding/json"
"fmt"
"log"
"net"
"net/http"
"strings"
"github.com/gorilla/mux"
)
اگر در نحوه نصب سردرگم هستید gorilla/mux
و این بسته چه کاری انجام می دهد، ساخت سرور با Go زیر 10 دقیقه را بخوانید. از هماکنون، کمی دیرتر APIهای REST را میسازیم. اول، اجازه دهید فرض کنیم که دریافت می کنیم domain
به عنوان رشته ای که باید روی آن کار کنیم.
تعریف یک کنترل کننده، isValidDomain
. کنترل کننده فقط یک پارامتر را می گیرد و می گوید:domain
از نوع رشته
func isValidDomain(domain string){
// controller code goes here
}
بیایید کار را با عملکرد کنترلر شروع کنیم. ابتدا، تعریف متغیرهایی برای بررسی اینکه آیا دامنه خاص دارای رکوردهای MX، رکوردهای SPF و رکوردهای DMARC است یا خیر. اگر دارند چه سوابقی دارند.
var hasMX, hasSPF, hasDMARC bool
var spfRecord string
var dmarcRecord string
اکنون زمان آن است که داده ها را در اینترنت بررسی کنید. بسته های فشرده داخلی Go، کار را انجام دهید net/http
بسته بندی در این صورت، شما باید از ruby یا nodej هایی استفاده می کردید که برای نصب باینری های بیشتر به آن نیاز داشتید.
mxRecords,err := net.LookupMX(domain)
if err != nil{
log.Printf("Error: %v\n",err)
}
if len(mxRecords)>0{
hasMX = true
}
ما از بسته نت برای دریافت آن استفاده می کنیم mxRecords
، در صورتی که داده ها را دریافت نکنیم، خطا دریافت می کنیم. خطا را در خطوط زیر بررسی می کنیم. برای بررسی اینکه آیا mxRecords را دریافت کردهایم، بررسی میکنیم که آیا طول آرایه بیش از یک است یا خیر، به این معنی که آیا آرایه حاوی بیش از یک عنصر است.
txtRecords, err := net.LookupTXT(domain)
if err != nil{
log.Printf("Error: %v\n",err)
}
for _, record := range txtRecords{
if strings.HasPrefix(record,"v=spf1"){
hasSPF = true
spfRecord = record
break
}
}
مشابه mxRecords، ما با استفاده از داده ها و خطاها را مدیریت می کنیم :=
اپراتور.
یاد آوردن،
:=
یک اعلامیه است، در حالی که=
یک اپراتور انتساب است.
پس از رسیدگی به خطا، از آن عبور می کنیم txtRecords
slice، از آنجایی که ما به ایندکس نیاز نداریم، آن را نادیده می گیریم _
. از آنجایی که Golang اجازه داشتن متغیرهای استفاده نشده را نمی دهد، از عملگر زیر خط استفاده می کنیم. هنگامی که ما حلقه بیش از txtRecords
، بررسی می کنیم که آیا نسخه رکورد زیر وجود دارد یا خیر spf1
. اگر چنین است، علامت گذاری می کنیم hasSPF
مثبت و ذخیره مقدار رکورد در spfRecord
.
dmarcRecords, err := net.LookupTXT("_dmarc." + domain)
if err != nil{
log.Printf("Error: %v\n",err)
}
for _, record := range dmarcRecords{
if strings.HasPrefix(record ,"v=DMARC1"){
hasDMARC = true
dmarcRecord = record
break
}
}
به دنبال dmarcRecords
تقریبا شبیه به txtRecords
. اضافه می کنیم _dmarc.
در پیشوند url دامنه، و از net
بسته ای برای بررسی اینکه آیا رکوردهای مربوطه برای dmarc وجود دارد یا خیر. هنگام عبور از برش dmarcRecords، بررسی می کنیم که آیا نسخه رکورد وجود دارد یا خیر DMARC1
hasDMARC true را علامت گذاری می کنیم و مقدار رکورد را در dmarcRecord ذخیره می کنیم.
برای بررسی اینکه آیا ما مقادیر و کنترل کننده خود را دریافت می کنیم isValidDomain
خوب کار می کند بیایید مقادیری را که تا کنون داشته ایم چاپ کنیم.
fmt.Printf("domain=%v\n,hasMX=%v\n,hasSPF=%v\n,spfRecord=%v\n,hasDMARC=%v\n,dmarcRecord=%v\n",domain,hasMX,hasSPF,spfRecord,hasDMARC,dmarcRecord)
تدوین isValidDomain
اسکریپت، ما داریم.
func isValidDomain(domain string){
var hasMX, hasSPF, hasDMARC bool
var spfRecord string
var dmarcRecord string
mxRecords,err := net.LookupMX(domain)
if err != nil{
log.Printf("Error: %v\n",err)
}
if len(mxRecords)>0{
hasMX = true
}
txtRecords, err := net.LookupTXT(domain)
if err != nil{
log.Printf("Error: %v\n",err)
}
for _, record := range txtRecords{
if strings.HasPrefix(record,"v=spf1"){
hasSPF = true
spfRecord = record
break
}
}
dmarcRecords, err := net.LookupTXT("_dmarc." + domain)
if err != nil{
log.Printf("Error: %v\n",err)
}
for _, record := range dmarcRecords{
if strings.HasPrefix(record ,"v=DMARC1"){
hasDMARC = true
dmarcRecord = record
break
}
}
fmt.Printf("domain=%v\n,hasMX=%v\n,hasSPF=%v\n,spfRecord=%v\n,hasDMARC=%v\n,dmarcRecord=%v\n",domain,hasMX,hasSPF,spfRecord,hasDMARC,dmarcRecord)
}
از آنجایی که، عملکرد ما کار می کند. بیایید شروع به کار روی ساخت API های REST خود کنیم. در ابتدا، ما 2 ساختار را تعریف خواهیم کرد، DomainURL
و DomainVar
. یکی برای رمزگشایی ورودی و دیگری برای رمزگذاری خروجی.
type DomainURL struct{
DomainURL string `string:"domainurl"`
}
type DomainVar struct{
Domain string `json:"domain"`
HasMX bool `json:"hasmx"`
HasSPF bool `json:"haspf"`
SpfRecord string `json:"spfrecord"`
HasDMARC bool `json:"hasdmarc"`
DmarcRecord string `json:"dmarcRecord"`
}
اگر شما یک توسعه دهنده جاوا اسکریپت هستید ساختار مشابه کلاس ES6 است. حال اجازه دهید یک برش را تعریف کنیم که شبیه بردارها در C++ است.
var domainVars []DomainVar
یک برش شبیه به یک آرایه است، تفاوت این است که وقتی می خواهید از آرایه ها در Golang استفاده کنید باید طول آن را مشخص کنید. به همین دلیل است که از یک برش استفاده می کنیم، همچنین به آن می گوییم که حاوی پست خواهد بود. در اینجا domainVars برشی از نوع DomainVar است. اضافه کردن مسیر برای درخواست POST در تابع اصلی.
ابتدا یک روتر درخواست جدید ایجاد کنید. روتر روتر اصلی برنامه وب ما است و بعداً به عنوان پارامتر به سرور ارسال می شود. تمام اتصالات HTTP را دریافت می کند و آن را به کنترل کننده های درخواستی که در آن ثبت می کنیم ارسال می کند. ما روتر را در تابع اصلی ایجاد می کنیم. پس از ثبت نام روتر، اجازه دهید نقاط پایانی را با استفاده از HandleFunction تعریف کنیم. ما HandleFunction را توسط r.HandleFunc(…) می نامیم.
func main(){
r := mux.NewRouter()
r.HandleFunc("/form",formHandler).Methods("POST")
fmt.Print("Starting server at port 8000\n")
log.Fatal(http.ListenAndServe(":8000",r))
}
در HandleFunc ما 2 پارامتر را ارائه می دهیم، اول مسیری که می خواهیم جادو را در آن ببینیم و ثانیا نام تابع کنترلر خاصی را می نویسیم که جادو را انجام می دهد. GET و POST متناظر به معنای عادی است، برای ساختن عملیات CRUD برای برنامه کافیست PUT و DELETE را با توجه به مسیر اضافه کنید.
پورت سرور را می توان به هر مقدار مورد نیاز منتقل کرد. اسکریپت برای POST Request Handler یا همان Handler یک مرحله در یک زمان.
func formHandler(w http.ResponseWriter, r *http.Request){
w.Header().Set("Content-Type","application/json")
}
ما هدر را برای کنترلر خاص در اینجا تعریف می کنیم. ما فقط هدر “Content-Type” را روی “application/json” تنظیم می کنیم.
func formHandler(w http.ResponseWriter, r *http.Request){
w.Header().Set("Content-Type","application/json")
var domainUrl DomainURL
json.NewDecoder(r.Body).Decode(&domainUrl)
}
اکنون مقدار دامنه را در تابع isValidDomain ارسال می کنیم. ما همه را در a ذخیره می کنیم domainVar
. پست کنید که domainVar را به آن اضافه می کنیم domainVars
تکه. سپس از بسته رمزگذاری برای رمزگذاری تمام داده های domainVars و همچنین برگرداندن آن در همان خط استفاده می کنیم. در نهایت، ارسال درخواست POST، واکشی داده ها، ذخیره سازی در یک ساختار، الحاق به برش و سپس برگرداندن برش.
func formHandler(w http.ResponseWriter, r *http.Request){
w.Header().Set("Content-Type","application/json")
var domainUrl DomainURL
json.NewDecoder(r.Body).Decode(&domainUrl)
domainVar := isValidDomain(domainUrl.DomainURL)
domainVars = append(domainVars, domainVar)
json.NewEncoder(w).Encode(domainVars)
}
از آنجایی که مسیر ما پیکربندی شده است، اجازه دهید تابع isValidDomain خود را طوری تغییر دهیم که بتوانیم مقادیر مورد نیاز برای ساختار DomainVar را بدست آوریم.
func isValidDomain(domain string) DomainVar{
var hasMX, hasSPF, hasDMARC bool
var spfRecord string
var dmarcRecord string
mxRecords,err := net.LookupMX(domain)
if err != nil{
log.Printf("Error: %v\n",err)
}
if len(mxRecords)>0{
hasMX = true
}
txtRecords, err := net.LookupTXT(domain)
if err != nil{
log.Printf("Error: %v\n",err)
}
for _, record := range txtRecords{
if strings.HasPrefix(record,"v=spf1"){
hasSPF = true
spfRecord = record
break
}
}
dmarcRecords, err := net.LookupTXT("_dmarc." + domain)
if err != nil{
log.Printf("Error: %v\n",err)
}
for _, record := range dmarcRecords{
if strings.HasPrefix(record ,"v=DMARC1"){
hasDMARC = true
dmarcRecord = record
break
}
}
fmt.Printf("domain=%v\n,hasMX=%v\n,hasSPF=%v\n,spfRecord=%v\n,hasDMARC=%v\n,dmarcRecord=%v\n",domain,hasMX,hasSPF,spfRecord,hasDMARC,dmarcRecord)
var domainVar DomainVar
domainVar.Domain = domain
domainVar.HasMX = hasMX
domainVar.HasSPF = hasSPF
domainVar.SpfRecord = spfRecord
domainVar.HasDMARC = hasDMARC
domainVar.DmarcRecord = dmarcRecord
return domainVar
}
تابع اصلاح شده دارای یک نوع بازگشتی است DomainVar
که در نهایت شیء از همان نوع را برمی گرداند. ما یک نمونه از نوع DomainVar ایجاد می کنیم و سپس شروع به تخصیص مقادیر برای هر کلید می کنیم. در نهایت شی را برگردانید.
*کد Backend کامل تا کنون این است: *
package main
import (
"encoding/json"
"fmt"
"log"
"net"
"net/http"
"strings"
"github.com/gorilla/mux"
)
type DomainURL struct{
DomainURL string `string:"domainurl"`
}
type DomainVar struct{
Domain string `json:"domain"`
HasMX bool `json:"hasmx"`
HasSPF bool `json:"haspf"`
SpfRecord string `json:"spfrecord"`
HasDMARC bool `json:"hasdmarc"`
DmarcRecord string `json:"dmarcRecord"`
}
var domainVars []DomainVar
func formHandler(w http.ResponseWriter, r *http.Request){
w.Header().Set("Content-Type","application/json")
var domainUrl DomainURL
json.NewDecoder(r.Body).Decode(&domainUrl)
domainVar := isValidDomain(domainUrl.DomainURL)
domainVars = append(domainVars, domainVar)
json.NewEncoder(w).Encode(domainVars)
}
func isValidDomain(domain string) DomainVar{
var hasMX, hasSPF, hasDMARC bool
var spfRecord string
var dmarcRecord string
mxRecords,err := net.LookupMX(domain)
if err != nil{
log.Printf("Error: %v\n",err)
}
if len(mxRecords)>0{
hasMX = true
}
txtRecords, err := net.LookupTXT(domain)
if err != nil{
log.Printf("Error: %v\n",err)
}
for _, record := range txtRecords{
if strings.HasPrefix(record,"v=spf1"){
hasSPF = true
spfRecord = record
break
}
}
dmarcRecords, err := net.LookupTXT("_dmarc." + domain)
if err != nil{
log.Printf("Error: %v\n",err)
}
for _, record := range dmarcRecords{
if strings.HasPrefix(record ,"v=DMARC1"){
hasDMARC = true
dmarcRecord = record
break
}
}
fmt.Printf("domain=%v\n,hasMX=%v\n,hasSPF=%v\n,spfRecord=%v\n,hasDMARC=%v\n,dmarcRecord=%v\n",domain,hasMX,hasSPF,spfRecord,hasDMARC,dmarcRecord)
var domainVar DomainVar
domainVar.Domain = domain
domainVar.HasMX = hasMX
domainVar.HasSPF = hasSPF
domainVar.SpfRecord = spfRecord
domainVar.HasDMARC = hasDMARC
domainVar.DmarcRecord = dmarcRecord
return domainVar
}
func main(){
r := mux.NewRouter()
r.HandleFunc("/form",formHandler).Methods("POST")
fmt.Print("Starting server at port 8000\n")
log.Fatal(http.ListenAndServe(":8000",r))
}
ساخت Frontend 🎨
ما در واقع روی زیباسازی برنامه تمرکز نخواهیم کرد، بلکه تمرکزمان بر انجام کارها خواهد بود. برای قسمت جلویی، ما به یک بسته نیاز داریم go-fiber
. اگرچه، میتوان frontend را بدون نصب هیچ بسته اضافی توسعه داد، دلیل استفاده از go-fiber درک نحوه نصب و کار با بستههای خارجی است.
بیایید با ایجاد دایرکتوری و تغییر دایرکتوری کاری خود به آن شروع کنیم.
mkdir verifier-frontend && cd verifier-frontend
راهاندازی فایل main.go برای frontend
touch main.go
با اجرای دستور، یک فایل خالی در دایرکتوری کاری ما در اینجا ایجاد می کند که verfier-frontend است.
باید برای فایل Go یک پکیج تعریف کنیم. از آنجایی که این فایل اصلی ما خواهد بود، بسته main را اضافه می کنیم.
package main
خط بالا اولین خط برنامه است.
حالا بیایید تمام بسته های لازم برای ساخت اپلیکیشن خود را وارد کنیم 🛍️
import (
"log"
"github.com/gofiber/fiber/v2"
)
اگر من جای شما بودم و همه واردات را می خواندم، خیلی گیج می شدم. اما، به من اعتماد کنید وقتی وبلاگ را خواندید، میتوانم شرط ببندم که ایده روشنی برای اینکه چرا بستههای زیر را گنجاندهایم، خواهید داشت.
ایجاد فایل go.mod که تمام بسته های مورد نیاز را ذخیره می کند، شبیه به package.json است.
go mod init github.com/Aniket762/namaste-go/verifier-front
برای اطمینان از اینکه فایل go.mod با کد منبع موجود در ماژول مطابقت دارد، اجرا می کنیم
go mod tidy
تمام بسته های وارد شده به جز یک مورد، قبلاً با فایل باینری که هنگام نصب Go اجرا کرده اید، وجود دارد. بنابراین، اجازه دهید Gorilla Mux را نصب کنیم.
go get github.com/gofiber/fiber/v2
اگر یک توسعه دهنده جاوا اسکریپت هستید، دستور زیر تقریباً شبیه npm install است
در حال حاضر، همه ما آماده ایم که شروع به ساخت ظاهر خود کنیم. بیایید کد را از اسناد رسمی Go Fiber دریافت کنیم، اجرا کنیم و آن را درک کنیم.
package main
import (
"log"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Get("https://dev.to/", func (c *fiber.Ctx) error {
return c.SendString("Hello From Verifier's Frontend 👋")
})
log.Fatal(app.Listen(":3000"))
}
پس از وارد کردن بسته ها، تابع اصلی را می نویسیم. تابع اصلی اولین تابعی است که با کامپایل و اجرای برنامه شروع می شود. ما یک نمونه، برنامه از نوع فیبر ایجاد می کنیم. ما مختصر c را برای متن می نویسیم.
درخواست و پاسخ HTTP در زمینه نگهداری می شود که توسط ساختار Ctx نشان داده می شود. متدهایی برای بدنه درخواست، هدرهای HTTP، آرگومان ها و رشته پرس و جو ارائه می کند.
بیایید سرور را در پورت 3000 اجرا کنیم. شما می توانید بنا به راحتی پورت را تغییر دهید. حالا اجازه دهید سرور را اجرا کنیم و بررسی کنیم که آیا همه چیز هماهنگ است یا خیر. به ترمینال خود بروید و اجرا کنید go run main.go
شما باید این را در ترمینال خود دریافت کنید که نشان می دهد پورتی که با آن در حال اجرا هستیم، تعداد کنترل کننده ها، شناسه های پردازش و تعداد فرآیندهایی که در حال اجرا هستیم. دادههای زیر زمانی که به سمت ساخت برنامههای پیچیدهتر حرکت میکنید، کاربرد واقعی دارند. هدایت به localhost:3000
یا هر پورتی که نوشته اید.
دیگر 💚
برنامه ای که ما توسعه داده ایم یک نمونه اولیه است و اصلاً آماده تولید نیست. تنها هدف وبلاگ این بود که به شما نحوه ساختن یک برنامه کامل پشته با بسته های بومی گو را به شما ارائه دهد. می توانید بیشتر تحقیق کنید و یک برنامه کاربردی آماده تولید بسازید.
اکنون، درست مانند هر آموزش دیگری، اجازه دهید آموزش را با یک کار به پایان برسانیم. ما با استفاده از API های REST توسعه داده ایم gorilla/mux
و استارتر جلویی با go-fiber
. سعی کنید مسیر POST را برای فرانتاند بسازید. در صورتی که قادر به ساختن نباشید، به زودی مقاله ای در مورد ساخت فرانت اند با Go-lang ارسال خواهم کرد. بهترین ها برای ساختن یک نمای کامل. اینک، وقتی من نحوه ساخت فرانت اند با استفاده از go-fiber را منتشر می کنم از دست ندهید من را در شبکه های اجتماعی من دنبال کنید ^-^
اگر برنامه را توسعه داده اید یا چیزی برای بحث در زیر نور خورشید دارید، می توانید با من در لینکدین تماس بگیرید یا توییتر 💖
اگر سازمانی را اداره می کنید و می خواهید که من فیلم های آموزشی بنویسم یا بسازم، لطفاً با من ارتباط برقرار کنید