当前位置:网站首页>Getting started with go web programming: validators
Getting started with go web programming: validators
2022-06-27 12:01:00 【Hua Weiyun】
Preface
Network authentication can be a challenge . There is a saying in Web Principles that are widely circulated in development :
We can't trust anything from the client user form .
So we have to validate all the incoming data before using it . Realization REST API yes Go Typical use cases for applications . API The accepted data with wrong format may cause serious errors in other parts of the system .
The best case scenario is that your database has mechanisms to prevent the storage of malformed data . If you don't do that , This data can cause errors and unexpected behavior in your customer facing applications ( such as SQL Inject ).
In this article , We'll show you how to Go Send validation to REST API The data of .
Manually verify the input
Simple REST API
This is an easy one REST API Example , Use gorilla/mux Package building . It's a great HTTP Router , Especially for REST API. API Provide a path for an endpoint /user. For the sake of simplicity , It only accepts... From all users HTTP GET And create a user's HTTP Post. Besides , It has no persistent database , Instead, you use slices to store users in memory .
package mainimport ( "encoding/json" "log" "net/http" "strings" "github.com/gorilla/mux")type User struct { ID int FirstName string LastName string FavouriteVideoGame string Email string}func main() { router := mux.NewRouter() router.HandleFunc("/user", PostUser).Methods(http.MethodPost) router.HandleFunc("/user", GetUsers).Methods(http.MethodGet) log.Fatal(http.ListenAndServe(":8081", router))}var users = []User{}var id = 0func validateEmail(email string) bool { // This is obviously not a good validation strategy for email addresses // pretend a complex regex here return !strings.Contains(email, "@")}func PostUser(w http.ResponseWriter, r *http.Request) { user := User{} json.NewDecoder(r.Body).Decode(&user) // We don't want an API user to set the ID manually // in a production use case this could be an automatically // ID in the database user.ID = id id++ users = append(users, user) w.WriteHeader(http.StatusCreated)}func GetUsers(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(users); err != nil { log.Println(err) w.WriteHeader(http.StatusInternalServerError) return }}Now let's look at how to manually validate what is provided to this in the request body API Of POST Input to the handler .
Manually verify the input
Sometimes we ask the user to enter some fields , But they failed to complete the field . For example, in the previous section , When we need a user name . You can use len Function to get the length of the field , To ensure that the user has entered something .
if len(r.Form["username"][0])==0{ // code for empty field }Suppose we want to use Post When the handler creates a user, it sets up FirstName、LastName and Email. Besides , We want the email field to be a valid email address . An easy way to do this is to manually validate fields , As shown below :
if user.FirstName == "" { errs = append(errs, fmt.Errorf("Firstname is required").Error())}if user.LastName == "" { errs = append(errs, fmt.Errorf("LastName is required").Error())}if user.Email == "" || validateEmail(user.Email) { errs = append(errs, fmt.Errorf("A valid Email is required").Error())}Complete example :
package mainimport ( "encoding/json" "fmt" "log" "net/http" "strings" "github.com/gorilla/mux")type User struct { ID int FirstName string LastName string FavouriteVideoGame string Email string}func main() { router := mux.NewRouter() router.HandleFunc("/user", PostUser).Methods(http.MethodPost) router.HandleFunc("/user", GetUsers).Methods(http.MethodGet) log.Fatal(http.ListenAndServe(":8081", router))}var users = []User{}var id = 0func validateEmail(email string) bool { // That's obviously not a good validation strategy for email addresses // pretend a complex regex here return !strings.Contains(email, "@")}func PostUser(w http.ResponseWriter, r *http.Request) { user := User{} json.NewDecoder(r.Body).Decode(&user) errs := []string{} if user.FirstName == "" { errs = append(errs, fmt.Errorf("Firstname is required").Error()) } if user.LastName == "" { errs = append(errs, fmt.Errorf("LastName is required").Error()) } if user.Email == "" || validateEmail(user.Email) { errs = append(errs, fmt.Errorf("A valid Email is required").Error()) } if len(errs) > 0 { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) if err := json.NewEncoder(w).Encode(errs); err != nil { } return } // We don't want an API user to set the ID manually // in a production use case this could be an automatically // ID in the database user.ID = id id++ users = append(users, user) w.WriteHeader(http.StatusCreated)}func GetUsers(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(users); err != nil { log.Println(err) w.WriteHeader(http.StatusInternalServerError) return }}You can see that this verification method is very verbose . We have to define a custom function to validate common things , Like email addresses . Let's see how to improve this .
It's just a simple pass validateEmail Function to verify whether the mailbox contains @ character :
func validateEmail(email string) bool { // That's obviously not a good validation strategy for email addresses // pretend a complex regex here return !strings.Contains(email, "@")}In fact, a better way is to use regular expressions to verify E-mail The effectiveness of the :
if m, _ := regexp.MatchString(`^([\w\.\_]{2,10})@(\w{1,}).([a-z]{2,4})$`, r.Form.Get("email")); !m { fmt.Println("no") }else{ fmt.Println("yes") }Use the structure tag to validate the input
stay Go A more common way to validate structures in is to use structure tags . There are many packages that perform structure validation through structure tags . We will use... Here https://github.com/go-playground/validator: The Validator Implement structure and single field value validation based on tags .

Use go get github.com/go-playground/validator/v10 Installation .
This not only enables us to use structure tags for verification , It also provides a number of predefined validation methods , An E-mail address, for example .
We will perform this verification on the structure , But it doesn't discuss how to fill the structure . We can assume that the data will be parsed JSON Payload 、 Enter explicit padding or other methods from the form to populate .
If your data needs another validator , Please check the documentation of the validator package . The verifier you need is most likely provided in the package 80 Under multiple validators .
package mainimport ( "encoding/json" "log" "net/http" "github.com/go-playground/validator/v10" "github.com/gorilla/mux")type User struct { ID int `validate:"isdefault"` FirstName string `validate:"required"` LastName string `validate:"required"` FavouriteVideoGame string Email string `validate:"required,email"`}func main() { router := mux.NewRouter() router.HandleFunc("/user", PostUser).Methods(http.MethodPost) router.HandleFunc("/user", GetUsers).Methods(http.MethodGet) log.Fatal(http.ListenAndServe(":8081", router))}var users = []User{}var id = 0func PostUser(w http.ResponseWriter, r *http.Request) { user := User{} json.NewDecoder(r.Body).Decode(&user) validate := validator.New() err := validate.Struct(user) if err != nil { validationErrors := err.(validator.ValidationErrors) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) responseBody := map[string]string{"error": validationErrors.Error()} if err := json.NewEncoder(w).Encode(responseBody); err != nil { } return } // We don't want an API user to set the ID manually // in a production use case this could be an automatically // ID in the database user.ID = id id++ users = append(users, user) w.WriteHeader(http.StatusCreated)}func GetUsers(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(users); err != nil { log.Println(err) w.WriteHeader(http.StatusInternalServerError) return }} above validationError.Error() Returns a string , This string summarizes each failed validation in the structure . therefore BadRequest The response still has very detailed information about what went wrong .
We changed validation to use the validator package , Now verify according to the following rules :
ID Fields should not be set by the user , So we verify that it has int The default value of , namely 0
FullName and LastName It's necessary
Email fields are required , And use a predefined email validator to verify
Use a custom validator to validate input
So now we use the validator package and use the structure tag to validate the structure . But how do we verify a structure field that cannot be verified by the tag provided by the library ?
Suppose we want to blacklist some video games . Because we don't want anyone in our system to like PUBG or Fortnite Waiting for game users . under these circumstances , We can define a custom validate Mark values and let validate The package uses it like this :
First, we define a validation function :
func GameBlacklistValidator(f1 validator.FieldLevel) bool { gameBlacklist := []string{"PUBG", "Fortnite"} game := f1.Field().String() for _, g := range gameBlacklist { if game == g { return false } } return true}Then we use the validator instance to register the function and the corresponding tag .
... validate := validator.New() validate.RegisterValidation("game-blacklist", GameBlacklistValidator)...Now we are User Add a label to the definition of the structure .
type User struct { ID int `validate:"isdefault"` FirstName string `validate:"required"` LastName string `validate:"required"` FavouriteVideoGame string `validate:"game-blacklist"` Email string `validate:"required,email"`}Recommended validation Libraries
Awesome Go Under the project, there are libraries for verification . Here are the recommendations :

govalidator - Validators and sanitizers for strings, numerics, slices and structs.
terraform-validator - A norms and conventions validator for Terraform.
validate - This package provides a framework for writing validations for Go applications.
According to the introduction, pick one of your own projects ~
summary
verification REST API Input is critical to prevent malformed data in your application . You can write your own validation logic , But in most cases , It is best to use a well maintained validation package , For example, the above recommendation .
This allows you to use tags in the structure to configure authentication , And keep the logic of running validation simple . If you have a special use case that requires unusual validation capabilities , You can still define your own extensions for the validator package .
Awesome Go:https://awesome-go.com/validation/
边栏推荐
- Jerry's DAC output mode setting [chapter]
- Four memory areas (stack, heap, global, code area)
- 内存四区(栈,堆,全局,代码区)
- Unity shader learning (I) understanding the basic structure of unity shader
- Histrix工作原理
- Mqtt protocol stack principle and interaction flow chart
- AutoCAD - three pruning methods
- 最大路径和问题(摘樱桃问题)
- 旭日3SDB,安装原版ros
- 15+ urban road element segmentation application, this segmentation model is enough!
猜你喜欢

57. The core principle of flutter - layout process

"Internet +" contest topic hot docking | I figure to understand 38 propositions of Baidu

Youboxun attended the openharmony technology day to create a new generation of secure payment terminals
![[tcaplusdb knowledge base] Introduction to tcaplusdb tcaplusadmin tool](/img/ba/f865c99f3ea9e42c85b7e906f4f076.png)
[tcaplusdb knowledge base] Introduction to tcaplusdb tcaplusadmin tool

【TcaplusDB知识库】TcaplusDB常规单据介绍

2022ciscn central China Web

1. Mx6ull startup mode

Jwas: a Bayesian based GWAS and GS software developed by Julia

Safe landing practice of software supply chain under salesforce containerized ISV scenario

Detailed explanation of interprocess communication
随机推荐
R语言fpc包的dbscan函数对数据进行密度聚类分析、plot函数可视化聚类图
.NET6接入Skywalking链路追踪完整流程
MQTT协议栈原理及交互流程图
Fork/Join 框架基本使用和原理
面试突击60:什么情况会导致 MySQL 索引失效?
QStyle实现自绘界面项目实战(一)
千万不要错过,新媒体运营15个宝藏公众号分享
The wonderful use of 0 length array in C language
Detailed explanation of interprocess communication
1. Mx6ull startup mode
[tcapulusdb knowledge base] tcapulusdb doc acceptance - Introduction to creating game area
面试突击60:什么情况会导致 MySQL 索引失效?
JSP custom tag
AutoCAD - three pruning methods
旭日3SDB,安装原版ros
Unity shader learning (I) understanding the basic structure of unity shader
器审科普:创新医疗器械系列科普——胸骨板产品
建木持续集成平台v2.5.0发布
Popular science of device review: popular science of innovative medical device series - sternum plate products
Summary of qstype class usage (II)