takeaway

Today we will explain the binding and verification of gin parameters. reference

Why validate parameters?

I don’t have to ask this question, but for those of you who are just starting out, here’s an explanation.

Suppose to do a registration interface, the user name can not be too SAO gas? Like a bunch of Spaces and symbols and things like that; Should passwords be neither too long nor too short? Does the mobile phone number have to comply with the rules? Is gender not allowed to fill in ladyboy?

In addition, when logging in, we also need to verify that the account password is correct, so to facilitate the use of a simple example, to do login authentication.

Passion to demonstrate

Before you log in, you must first think about what restrictions you need to make on the user name and password. For example, they can not be empty, the user name can only be letters or numbers, the password length can only be between 6 and 12 characters, etc. If you see no objection, I will take the above conditions to demonstrate.

Define structures and interfaces

The first thing you need is a place to store the username and password parameters you receive. Use a structure called Login.

type Login struct {
	User     string
	Password string
}
Copy the code

Then define a login interface. If you don’t know what it means, you can go to my second tutorial.

router.POST("/login".func(c *gin.Context){})Copy the code

The interface is fixed. How can I put the received parameters into the Login structure?

Binding parameters

Let’s take a look at gin.Context and see what good things you can do with it. Bind, which automatically parses the requested parameters according to the content-Type values and then sets them to the structure.

func (c *Context) Bind(obj interface{}) error {
	b := binding.Default(c.Request.Method, c.ContentType())
	return c.MustBindWith(obj, b)
}
Copy the code

Wow, this is interesting. Let’s call it. Remember to pass in the reference to Login, because they have to assign.

router.POST("/login".func(c *gin.Context) {
  var login Login
  c.Bind(&login)
  c.JSON(200, login)
})
Copy the code

And sure enough, those of you who are running should be able to see that it failed, that it didn’t get the value that I wanted.

curl -d "user=pingye&password=123" http://localhost:8080/login
{"User":""."Password":""}
Copy the code

Binding failed, parse the source code

The Bind method we called actually calls bind. Default and c. mustbindwith. The former is used to select the processor based on the content-type requested by the terminal, but gin is too strong and supports too many types. The request we just made was duly assigned to a formBinding.

var (
	JSON          = jsonBinding{}
	XML           = xmlBinding{}
	Form          = formBinding{}
	Query         = queryBinding{}
	FormPost      = formPostBinding{}
	FormMultipart = formMultipartBinding{}
	ProtoBuf      = protobufBinding{}
	MsgPack       = msgpackBinding{}
	YAML          = yamlBinding{}
	Uri           = uriBinding{}
	Header        = headerBinding{}
)
Copy the code

And then what? C. mustbindwith (Bind, Bind, Bind, Bind, Bind);

func (formBinding) Bind(req *http.Request, obj interface{}) error {
	iferr := req.ParseForm(); err ! = nil {return err
	}
	iferr := req.ParseMultipartForm(defaultMemory); err ! = nil {iferr ! = http.ErrNotMultipart {return err
		}
	}
	iferr := mapForm(obj, req.Form); err ! = nil {return err
	}
	return validate(obj)
}
Copy the code

The Bind method does two main things. First, it parses the form parameters passed in, and second, it finds the tagForm in the structure and assigns a matching value. All I need to do is put a tag after Login.

Continue binding parameters

So let’s try it out with a tag.

type Login struct {
	User     string `form:"user"`
	Password string `form:"password"`
}
Copy the code

It ran perfectly.

curl -d "user=pingye&password=123" http://localhost:8080/login
{"User":"pingye"."Password":"123"}
Copy the code

See here, you should have a lot of ideas, just said that support so many types, front-end is the json why? The jsonBinding does not look for the tag in the Login structure, so you do not need to add the JSON :”user” tag at the end.

curl -H "Content-Type:application/json" -d '{"user":"pingye","password":"123455"}' http://localhost:8080/login
{"User":"pingye"."Password":"123455"}
Copy the code

As for the other types, students can experiment with their own hands, we must get the parameter verification link.

Parameter validation

OK, this is the exciting parameter validation time. To review the requirements, the username and password cannot be empty, the username must be English and numeric, and the password must be 6 to 12 characters long.

Gin’s official example is to add validation rules directly to a tag, such as binding:”required” if it cannot be null.

type Login struct {
	User     string `form:"user" binding:"required"`
	Password string `form:"password" binding:"required"`
}
Copy the code

Bind will automatically check for us. If the check fails, it will return an error.

router.POST("/login".func(c *gin.Context) {
  var login Login
  err := c.Bind(&login)
  iferr ! = nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    return
  }
  c.JSON(200, login)
})
Copy the code

Rest assured, the demonstration of parameter validation won’t end there.

Given the lack of information provided by the official documentation, let’s look for more clues in the source code, which reveals that Gin’s parameter validation is not actually implemented by itself, but instead uses a library called Go-Playground/Validator.

. ├ ─ ─ LICENSE ├ ─ ─ a Makefile ├ ─ ─ the README. Md ├ ─ ─ _examples ├ ─ ─ baked_in. Go ├ ─ ─ benchmarks_test. Go ├ ─ ─ cache. Go ├ ─ ─ doc. Go Do you have to do something else? Do you have to do something else? Do you have to do something else ├── Exercises for exercises ── exercises for exercises, exercises for exercises ── Exercises for exercises, exercises for exercises, exercises for exercises, exercises for exercises, exercises for exercises, exercises for exercises, exercises for exercises, exercises for exercises, exercises for exercises validator_test.go4 directories, 19 files
Copy the code

There is a file called doc.go, which has a treasure trove of examples and explanations. Fuck the official documentation.

I found the demo with the length limit in it. It’s very simple, just two tags min and Max. There’s nothing wrong with a run.

type Login struct {
	User     string `form:"user" binding:"required"`
	Password string `form:"password" binding:"required,min=6,max=12"`
}
curl -d "user=pingye&password=12345" http://localhost:8080/login 
{"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'min' tag"}

curl -d "user=pingye&password=1234567890123" http://localhost:8080/login
{"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'max' tag"}
Copy the code

Was implemented two check and also the user name of letters and Numbers, let me shocked, I thought just said this limit, want to use a variety of combinations had found a corresponding, awesome behind (strongly recommend this library, it is necessary to a phase of the introduction of the library), is very simple, Add a rule called Alphanum to do this.

type Login struct {
	User     string `form:"user" binding:"required,alphanum"`
	Password string `form:"password" binding:"required,min=6,max=12"`
}
Copy the code

In this paper, to participate in