Merging other languages
This commit is contained in:
303
en/04.2.md
303
en/04.2.md
@@ -1,162 +1,141 @@
|
||||
# 4.2 验证表单的输入
|
||||
|
||||
开发Web的一个原则就是,不能信任用户输入的任何信息,所以验证和过滤用户的输入信息就变得非常重要,我们经常会在微博、新闻中听到某某网站被入侵了,存在什么漏洞,这些大多是因为网站对于用户输入的信息没有做严格的验证引起的,所以为了编写出安全可靠的Web程序,验证表单输入的意义重大。
|
||||
|
||||
我们平常编写Web应用主要有两方面的数据验证,一个是在页面端的js验证(目前在这方面有很多的插件库,比如ValidationJS插件),一个是在服务器端的验证,我们这小节讲解的是如何在服务器端验证。
|
||||
|
||||
## 必填字段
|
||||
你想要确保从一个表单元素中得到一个值,例如前面小节里面的用户名,我们如何处理呢?Go有一个内置函数`len`可以获取字符串的长度,这样我们就可以通过len来获取数据的长度,例如:
|
||||
|
||||
if len(r.Form["username"][0])==0{
|
||||
//为空的处理
|
||||
}
|
||||
|
||||
`r.Form`对不同类型的表单元素的留空有不同的处理, 对于空文本框、空文本区域以及文件上传,元素的值为空值,而如果是未选中的复选框和单选按钮,则根本不会在r.Form中产生相应条目,如果我们用上面例子中的方式去获取数据时程序就会报错。所以我们需要通过`r.Form.Get()`来获取值,因为如果字段不存在,通过该方式获取的是空值。但是通过`r.Form.Get()`只能获取单个的值,如果是map的值,必须通过上面的方式来获取。
|
||||
|
||||
## 数字
|
||||
你想要确保一个表单输入框中获取的只能是数字,例如,你想通过表单获取某个人的具体年龄是50岁还是10岁,而不是像“一把年纪了”或“年轻着呢”这种描述
|
||||
|
||||
如果我们是判断正整数,那么我们先转化成int类型,然后进行处理
|
||||
|
||||
getint,err:=strconv.Atoi(r.Form.Get("age"))
|
||||
if err!=nil{
|
||||
//数字转化出错了,那么可能就不是数字
|
||||
}
|
||||
|
||||
//接下来就可以判断这个数字的大小范围了
|
||||
if getint >100 {
|
||||
//太大了
|
||||
}
|
||||
|
||||
还有一种方式就是正则匹配的方式
|
||||
|
||||
if m, _ := regexp.MatchString("^[0-9]+$", r.Form.Get("age")); !m {
|
||||
return false
|
||||
}
|
||||
|
||||
对于性能要求很高的用户来说,这是一个老生常谈的问题了,他们认为应该尽量避免使用正则表达式,因为使用正则表达式的速度会比较慢。但是在目前机器性能那么强劲的情况下,对于这种简单的正则表达式效率和类型转换函数是没有什么差别的。如果你对正则表达式很熟悉,而且你在其它语言中也在使用它,那么在Go里面使用正则表达式将是一个便利的方式。
|
||||
|
||||
>Go实现的正则是[RE2](http://code.google.com/p/re2/wiki/Syntax),所有的字符都是UTF-8编码的。
|
||||
|
||||
## 中文
|
||||
有时候我们想通过表单元素获取一个用户的中文名字,但是又为了保证获取的是正确的中文,我们需要进行验证,而不是用户随便的一些输入。对于中文我们目前有两种方式来验证,可以使用 `unicode` 包提供的 `func Is(rangeTab *RangeTable, r rune) bool` 来验证,也可以使用正则方式来验证,这里使用最简单的正则方式,如下代码所示
|
||||
|
||||
if m, _ := regexp.MatchString("^\\p{Han}+$", r.Form.Get("realname")); !m {
|
||||
return false
|
||||
}
|
||||
|
||||
## 英文
|
||||
我们期望通过表单元素获取一个英文值,例如我们想知道一个用户的英文名,应该是astaxie,而不是asta谢。
|
||||
|
||||
我们可以很简单的通过正则验证数据:
|
||||
|
||||
if m, _ := regexp.MatchString("^[a-zA-Z]+$", r.Form.Get("engname")); !m {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
## 电子邮件地址
|
||||
你想知道用户输入的一个Email地址是否正确,通过如下这个方式可以验证:
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
|
||||
## 手机号码
|
||||
你想要判断用户输入的手机号码是否正确,通过正则也可以验证:
|
||||
|
||||
if m, _ := regexp.MatchString(`^(1[3|4|5|8][0-9]\d{4,8})$`, r.Form.Get("mobile")); !m {
|
||||
return false
|
||||
}
|
||||
|
||||
## 下拉菜单
|
||||
如果我们想要判断表单里面`<select>`元素生成的下拉菜单中是否有被选中的项目。有些时候黑客可能会伪造这个下拉菜单不存在的值发送给你,那么如何判断这个值是否是我们预设的值呢?
|
||||
|
||||
我们的select可能是这样的一些元素
|
||||
|
||||
<select name="fruit">
|
||||
<option value="apple">apple</option>
|
||||
<option value="pear">pear</option>
|
||||
<option value="banane">banane</option>
|
||||
</select>
|
||||
|
||||
那么我们可以这样来验证
|
||||
|
||||
slice:=[]string{"apple","pear","banane"}
|
||||
|
||||
for _, v := range slice {
|
||||
if v == r.Form.Get("fruit") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
## 单选按钮
|
||||
如果我们想要判断radio按钮是否有一个被选中了,我们页面的输出可能就是一个男、女性别的选择,但是也可能一个15岁大的无聊小孩,一手拿着http协议的书,另一只手通过telnet客户端向你的程序在发送请求呢,你设定的性别男值是1,女是2,他给你发送一个3,你的程序会出现异常吗?因此我们也需要像下拉菜单的判断方式类似,判断我们获取的值是我们预设的值,而不是额外的值。
|
||||
|
||||
<input type="radio" name="gender" value="1">男
|
||||
<input type="radio" name="gender" value="2">女
|
||||
|
||||
那我们也可以类似下拉菜单的做法一样
|
||||
|
||||
slice:=[]int{1,2}
|
||||
|
||||
for _, v := range slice {
|
||||
if v == r.Form.Get("gender") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
## 复选框
|
||||
有一项选择兴趣的复选框,你想确定用户选中的和你提供给用户选择的是同一个类型的数据。
|
||||
|
||||
<input type="checkbox" name="interest" value="football">足球
|
||||
<input type="checkbox" name="interest" value="basketball">篮球
|
||||
<input type="checkbox" name="interest" value="tennis">网球
|
||||
|
||||
对于复选框我们的验证和单选有点不一样,因为接收到的数据是一个slice
|
||||
|
||||
slice:=[]string{"football","basketball","tennis"}
|
||||
a:=Slice_diff(r.Form["interest"],slice)
|
||||
if a == nil{
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
|
||||
上面这个函数`Slice_diff`包含在我开源的一个库里面(操作slice和map的库),[https://github.com/astaxie/beeku](https://github.com/astaxie/beeku)
|
||||
|
||||
## 日期和时间
|
||||
你想确定用户填写的日期或时间是否有效。例如
|
||||
,用户在日程表中安排8月份的第45天开会,或者提供未来的某个时间作为生日。
|
||||
|
||||
Go里面提供了一个time的处理包,我们可以把用户的输入年月日转化成相应的时间,然后进行逻辑判断
|
||||
|
||||
t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
|
||||
fmt.Printf("Go launched at %s\n", t.Local())
|
||||
|
||||
获取time之后我们就可以进行很多时间函数的操作。具体的判断就根据自己的需求调整。
|
||||
|
||||
## 身份证号码
|
||||
如果我们想验证表单输入的是否是身份证,通过正则也可以方便的验证,但是身份证有15位和18位,我们两个都需要验证
|
||||
|
||||
//验证15位身份证,15位的是全部数字
|
||||
if m, _ := regexp.MatchString(`^(\d{15})$`, r.Form.Get("usercard")); !m {
|
||||
return false
|
||||
}
|
||||
|
||||
//验证18位身份证,18位前17位为数字,最后一位是校验位,可能为数字或字符X。
|
||||
if m, _ := regexp.MatchString(`^(\d{17})([0-9]|X)$`, r.Form.Get("usercard")); !m {
|
||||
return false
|
||||
}
|
||||
|
||||
上面列出了我们一些常用的服务器端的表单元素验证,希望通过这个引导入门,能够让你对Go的数据验证有所了解,特别是Go里面的正则处理。
|
||||
|
||||
## links
|
||||
* [目录](<preface.md>)
|
||||
* 上一节: [处理表单的输入](<04.1.md>)
|
||||
* 下一节: [预防跨站脚本](<04.3.md>)
|
||||
# 4.2 Verification of inputs
|
||||
|
||||
One of the most important principles in web development is that you cannot trust anything from client side user forms. You have to verify all incoming data before use it. Many websites are affected by this problem, which is simple yet crucial.
|
||||
|
||||
There are two ways of verify form data that are commonly used. One is JavaScript verification in the front-end, and the other is server verification in the back-end. In this section, we are going to talk about server side verification in web development.
|
||||
|
||||
## Required fields
|
||||
|
||||
Sometimes we require that users input some fields but they don't, for example in the previous section when we required a username. You can use the `len` function to get the length of a field in order to ensure that users have entered this information.
|
||||
|
||||
if len(r.Form["username"][0])==0{
|
||||
// code for empty field
|
||||
}
|
||||
|
||||
`r.Form` treats different form element types differently when they are blank. For empty textboxes, text areas and file uploads, it returns an empty string; for radio buttons and check boxes, it doesn't even create the corresponding items. Instead, you will get errors if you try to access it. Therefore, it's safer to use `r.Form.Get()` to get filed values since it will always return empty if the value does not exist. On the other hand, `r.Form.Get()` can only get one field value at a time, so you need to use `r.Form` to get the map of values.
|
||||
|
||||
## Numbers
|
||||
|
||||
Sometimes you only need numbers for the field value. For example, let's say that you require the age of a user in integer form only, i.e 50 or 10, instead of "old enough" or "young man". If we require a positive number, we can convert the value to the `int` type first, then process it.
|
||||
|
||||
getint,err:=strconv.Atoi(r.Form.Get("age"))
|
||||
if err!=nil{
|
||||
// error occurs when convert to number, it may not a number
|
||||
}
|
||||
|
||||
// check range of number
|
||||
if getint >100 {
|
||||
// too big
|
||||
}
|
||||
|
||||
Another way to do this is using regular expressions.
|
||||
|
||||
if m, _ := regexp.MatchString("^[0-9]+$", r.Form.Get("age")); !m {
|
||||
return false
|
||||
}
|
||||
|
||||
For high performance purposes, regular expressions are not efficient, however simple regular expressions are usually fast enough. If you are familiar with regular expressions, it's a very convenient way to verify data. Notice that Go uses [RE2](http://code.google.com/p/re2/wiki/Syntax), so all UTF-8 characters are supported.
|
||||
|
||||
## Chinese
|
||||
|
||||
Sometimes we need users to input their Chinese names and we have to verify that they all use Chinese rather than random characters. For Chinese verification, regular expressions are the only way.
|
||||
|
||||
if m, _ := regexp.MatchString("^[\\x{4e00}-\\x{9fa5}]+$", r.Form.Get("realname")); !m {
|
||||
return false
|
||||
}
|
||||
|
||||
## English letters
|
||||
|
||||
Sometimes we need users to input only English letters. For example, we require someone's English name, like astaxie instead of asta谢. We can easily use regular expressions to perform our verification.
|
||||
|
||||
if m, _ := regexp.MatchString("^[a-zA-Z]+$", r.Form.Get("engname")); !m {
|
||||
return false
|
||||
}
|
||||
|
||||
## E-mail address
|
||||
|
||||
If you want to know whether users have entered valid E-mail addresses, you can use the following regular expression:
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
## Drop down list
|
||||
|
||||
Let's say we require an item from our drop down list, but instead we get a value fabricated by hackers. How do we prevent this from happening?
|
||||
|
||||
Suppose we have the following `<select>`:
|
||||
|
||||
<select name="fruit">
|
||||
<option value="apple">apple</option>
|
||||
<option value="pear">pear</option>
|
||||
<option value="banana">banana</option>
|
||||
</select>
|
||||
|
||||
We can use the following strategy to sanitize our input:
|
||||
|
||||
slice:=[]string{"apple","pear","banana"}
|
||||
|
||||
for _, v := range slice {
|
||||
if v == r.Form.Get("fruit") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
All the functions I've shown above are in my open source project for operating on slices and maps: [https://github.com/astaxie/beeku](https://github.com/astaxie/beeku)
|
||||
|
||||
## Radio buttons
|
||||
|
||||
If we want to know whether the user is male or female, we may use a radio button, returning 1 for male and 2 for female. However, some little kid who just read his first book on HTTP, decides to send to you a 3. Will your program have have exception? As you can see, we need to use the same method as we did for our drop down list to make sure that only expected values are returned by our radio button.
|
||||
|
||||
<input type="radio" name="gender" value="1">Male
|
||||
<input type="radio" name="gender" value="2">Female
|
||||
|
||||
And we use following code to verify the input:
|
||||
|
||||
slice:=[]int{1,2}
|
||||
|
||||
for _, v := range slice {
|
||||
if v == r.Form.Get("gender") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
## Check boxes
|
||||
|
||||
Suppose there are some check boxes for user interests, and that you don't want extraneous values here either.
|
||||
|
||||
<input type="checkbox" name="interest" value="football">Football
|
||||
<input type="checkbox" name="interest" value="basketball">Basketball
|
||||
<input type="checkbox" name="interest" value="tennis">Tennis
|
||||
|
||||
In this case, the sanitization is a little bit different than verifying the button and check box inputs since here we get a slice from the check boxes.
|
||||
|
||||
slice:=[]string{"football","basketball","tennis"}
|
||||
a:=Slice_diff(r.Form["interest"],slice)
|
||||
if a == nil{
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
|
||||
## Date and time
|
||||
|
||||
Suppose you want users to input valid dates or times. Go has the `time` package for converting year, month and day to their corresponding times. After that, it's easy to check it.
|
||||
|
||||
t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
|
||||
fmt.Printf("Go launched at %s\n", t.Local())
|
||||
|
||||
After you have the time, you can use the `time` package for more operations, depending on your needs.
|
||||
|
||||
In this section, we've discussed some common methods for verifying form data server side. I hope that you now understand more about data verification in Go, especially how to use regular expressions to your advantage.
|
||||
|
||||
## Links
|
||||
|
||||
- [Directory](preface.md)
|
||||
- Previous section: [Process form inputs](04.1.md)
|
||||
- Next section: [Cross site scripting](04.3.md)
|
||||
|
||||
Reference in New Issue
Block a user