This article was originally posted on ISLAND
In the previous chapter, we started to build a Web page gradually. Now we start to improve the functions of the page step by step, starting with the login and registration functions.
Accept form data
The HTML element of the registration page is not written in detail. The specific page code can refer to the code on Github directly.
Page layout after completion:
The registration page has three input fields: Email, Password and Password Again.
Improve the back-end Gin code. We write the new interface in userGroup in initRouter.
userRouter.POST("/register", handler.UserRegister)
Copy the code
Once you’ve written the new interface, start writing the Handler.
func UserRegister(context *gin.Context) {
email := context.PostForm("email")
password := context.DefaultPostForm("password"."Wa123456")
passwordAgain := context.DefaultPostForm("password-again"."Wa123456")
println("email", email, "password", password, "password again", passwordAgain)
}
Copy the code
The UserRegister method takes a new approach to accepting form parameters submitted by Post requests, PostForm and DefaultPostForm. PostForm accepts arguments directly, while DefaultPostForm can set a default value. If the front end does not send a value, we can set a default value. If the front end does not send a password, we can set a default password.
As we run and type, our input on the form is clearly visible on the console.
When our project is fully functional, we can improve our unit tests.
The unit tests at this point are a little more complicated before delivery.
First we need to construct a structure to help us put the information we want to submit into the form and specify the request header information.
func TestUserPostForm(t *testing.T) {
value := url.Values{}
value.Add("email"."[email protected]")
value.Add("password"."1234")
value.Add("password-again"."1234")
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/user/register", bytes.NewBufferString(value.Encode()))
req.Header.Add("Content-Type"."application/x-www-form-urlencoded; param=value")
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
}
Copy the code
After the unit test is written, we can run the unit test and find that the console agrees with the data we wrote during the test.
Model binding
In the example above, our form only transmitted three parameters. If more than ten parameters appeared in the later project, it would take time and experience to write each time. Here’s how to improve this method
Gin provides model binding to bind our form data the same as our model. GIn encapsulates the data in a unified model for future use.
First define our model, create a new model folder, and create userModel.go
package model
type UserModel struct {
Email string `form:"email"`
Password string `form:"password"`
PasswordAgain string `form:"password-again"`
}
Copy the code
Use form:”email” to bind the email input data in the form. Then you need to modify the Handler method.
func UserRegister(context *gin.Context) {
var user model.UserModel
iferr := context.ShouldBind(&user); err ! =nil {
println("err ->", err.Error())
return
}
println("email", user.Email, "password", user.Password, "password again", user.PasswordAgain)
}
Copy the code
Now that our model binding is written, run the TestUserPostForm test case, and the test case passes flawlessly. It shows that our model binding method is correct. Model binding is also the binding of data from JSON, XML, YML, etc., which will be explained later. It can also be submitted via the registration form in the browser.
Data validation
Backend developers know one thing: never trust data from the front end. All data must be checked before entering the back end.
Binding can be used to validate data in the model. Gin uses the Validator.v8 library for data validation, which provides a variety of validation methods. Verify data using binding:””.
We will modify the UserModel to add some rules, mailbox authentication and password verification, requiring the second repeat password to be the same as the first password. More verification rules can be found in the official documentation
type UserModel struct {
Email string `form:"email" binding:"email"`
Password string `form:"password"`
PasswordAgain string `form:"password-again" binding:"eqfield=Password"`
}
Copy the code
Let’s write a new test case to test whether mailbox and password verification are valid.
func TestUserPostFormEmailErrorAndPasswordError(t *testing.T) {
value := url.Values{}
value.Add("email"."youngxhui")
value.Add("password"."1234")
value.Add("password-again"."qwer")
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/user/register", bytes.NewBufferString(value.Encode()))
req.Header.Add("Content-Type"."application/x-www-form-urlencoded; param=value")
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
}
Copy the code
Run the test and find that the test passed, but there are two lines of error messages
err -> Key: 'UserModel.Email' Error:Field validation for 'Email' failed on the 'email' tag
Key: 'UserModel.PasswordAgain' Error:Field validation for 'PasswordAgain' failed on the 'eqfield' tag
Copy the code
This message indicates that our Email and PasswordAgain messages did not pass the verification.
Use Log and redirection
The test passed because no matter what our code did, it would return a 200 status code, which is not compliant with the HTTP status code specification, so we need to normalize the HTTP status code. At the same time, our previous code has been using Printf to print Log information, which is also not standard. Because the Log information Printf prints is relatively limited, Log should be used for Log printing.
func UserRegister(context *gin.Context) {
var user model.UserModel
iferr := context.ShouldBind(&user); err ! =nil {
log.Println("err ->", err.Error())
context.String(http.StatusBadRequest, "The data entered is invalid.")}else {
log.Println("email", user.Email, "password", user.Password, "password again", user.PasswordAgain)
context.Redirect(http.StatusMovedPermanently, "/")}}Copy the code
First of all, we’re going to print all the data that we used to print only in Println and we’re going to print all the data in log.
At the same time, the original status codes are changed, and different status codes represent different response results.
Finally, when the request was successful, we redirected the route to the home page.
We also need to modify the return status code in the test case.
conclusion
This section provides a relatively detailed introduction to form submission, model binding, and data validation, as well as various test cases in the code to check for code correctness.
Code for this section
Github
Index of Other Sections
Gin(I):Hello Gin(II): Routing Router Gin(III): Template TMPL Gin(IV): Form submission checksum model binding Gin(V): Connection to MySQL Gin(VI): File upload Gin(VII): Use and definition of middleware Gin(8): Use of cookies
Personal public account
The latest articles will be shared on the public account, welcome to pay attention to