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