学习笔记 golang
Web服务器
我们看到上面的代码,要编写一个Web服务器很简单,只要调用http包的两个函数就可以了;如果你以前是PHP程序员,那你也许就会问,我们的nginx、apache服务器不需要吗?Go就是不需要这些,因为他直接就监听tcp端口了,做了nginx做的事情,然后sayhelloName这个其实就是我们写的逻辑函数了,跟php里面的控制层(controller)函数类似
代码语言:javascript复制func httpTest(w http.ResponseWriter, r *http.Request) {
/*
fmt.Println(w)
*/
/*
fmt.Println(r)
*/
// http://localhost:89/index?name=shyzhen&password=123456
r.ParseForm() // 解析参数,默认是不会解析的
fmt.Println(r.Form) // URL携带的参数 (必须执行上一步解析参数,不然是空map[]) `map[name:[shyzhen] password:[123456]]`
fmt.Println("URL:", r.URL) // 不包括host的url, `/index?name=shyzhen&password=123456`
fmt.Println("URL.paht:", r.URL.Path) // 当前路由 `/index`
fmt.Println("URL.Scheme", r.URL.Scheme)
fmt.Println(r.Form["name"][0]) // shyzhen
fmt.Println("----------------")
for k, v := range r.Form {
fmt.Println(k, v)
}
fmt.Fprintln(w, "hello world") // 这个写入到w的是输出到客户端的
}
func main() {
http.HandleFunc("/index", httpTest)
err := http.ListenAndServe(":9527", nil)
if err != nil {
log.Fatal("listenandserve:", err)
}
}
通过server.go源码可以看到go语言在每个请求都使用了goroutines,保证每个请求独立,相互不会阻塞。
代码语言:javascript复制func (srv *Server) Serve(l net.Listener) error {
...
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
...
}
web服务器案例
- login.gtpl
<html>
<head>
<title></title>
</head>
<body>
<form action="/login" method="post">
用户名:<input type="text" name="username">
密码: <input type="password" name="password">
<input type="submit" value="登录">
</form>
</body>
</html>
- login.go
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "首页hello")
}
func login(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.Method)
if r.Method == "GET" { // 根据访问方法类型渲染不同界面和逻辑
//fmt.Fprintln(w, "登录界面") // 引入模板的话,不能有输出
t, _ := template.ParseFiles("./view/login.gtpl") // ParseFiles引入模板文件,返回模板和错误信息
err := t.Execute(w, nil) // 渲染模板
log.Println("err:", err) // 在服务端打印日志
//log.Println("err:", t.Execute(w, nil)) // 简洁写法
} else {
r.ParseForm() // 解析参数,默认是不会解析的
fmt.Println(r.Form["username"])
fmt.Println(r.Form["password"])
// 生成url参数
/ *
v := url.Values{}
v.Set("name", "Ava")
v.Add("friend", "Jess")
v.Add("friend", "Sarah")
v.Add("friend", "Zoe")
fmt.Println("v:", v)
fmt.Println("get name :", v.Get("name"))
fmt.Println("get friend:", v.Get("friend"))
fmt.Println("friend", v["friend"])
fmt.Println("v.Encode()", v.Encode())
*/
}
}
func main() {
http.HandleFunc("/", hello)
http.HandleFunc("/login", login)
err := http.ListenAndServe(":9527", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
Validator
常见的验证规则:是否必填(len())、数字、长度、中英文、电子邮件、手机号码等:
代码语言:javascript复制<html>
<head>
<title></title>
</head>
<body>
<form action="/login" method="post">
用户名:<input type="text" name="username">
密码: <input type="password" name="password">
验证码:<input type="text" name="verifycode">
爱好 :<input type="checkbox" name="like" value="1">1
<input type="checkbox" name="like" value="2">2
<input type="checkbox" name="like" value="3">3
<input type="submit" value="登录">
</form>
</body>
</html>
代码语言:javascript复制func login(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
if r.Method == "GET" {
t, _ := template.ParseFiles("./view/validator.gtpl")
log.Println(t.Execute(w, nil))
} else {
r.ParseForm()
//username := r.Form["username"][0]
//password := r.Form["password"][0]
//verifycode := r.Form["verifycode"][0]
like := r.Form["like"] // checkBox必须用这种方式取值 (通过r.Form.Get()只能获取单个的值,如果是map的值,必须通过该方式来获取)
username := r.Form.Get("username")
password := r.Form.Get("password")
verifycode := r.Form.Get("verifycode")
//like := r.Form.Get("like")
fmt.Println(username, password, verifycode, like)
// 输入验证
if len(username) == 0 {
fmt.Println("用户名不能为空")
}
if len(password) < 6 {
fmt.Println("密码必须大于等于6位字符")
}
if !isNumber(verifycode) {
fmt.Println("验证码只能是数字")
}
}
}
// 数字
func isNumber(number1 string) bool {
if m, _ := regexp.MatchString("^[0-9] $", number1); !m {
return false
}
return true
}
// 中文
func isChinese(string1 string) bool {
if m, _ := regexp.MatchString("^\p{Han} $", string1); !m {
return false
}
return true
}
// 英文
func isEnglish(string1 string) bool {
if m, _ := regexp.MatchString("^[a-zA-Z] $", string1); !m {
return false
}
return true
}
// email
func isEmail(string1 string) bool {
if m, _ := regexp.MatchString(`^([w._]{2,10})@(w{1,}).([a-z]{2,4})$`, string1); !m {
return false
}
return true
}
// 手机号码
func isPhone(string1 string) bool {
if m, _ := regexp.MatchString(`^(1[3|4|5|8][0-9]d{4,8})$`, string1); !m {
return false
}
return true
}
// 身份证
func isIDCard(string1 string) bool {
// 验证15位身份证,15位的是全部数字
//if m, _ := regexp.MatchString(`^(d{15})$`, string1); !m {
// return false
//}
// 验证18位身份证,18位前17位为数字,最后一位是校验位,可能为数字或字符X。
if m, _ := regexp.MatchString(`^(d{17})([0-9]|X)$`, string1); !m {
return false
}
return true
}
func main() {
http.HandleFunc("/", login)
err := http.ListenAndServe(":9527", nil)
if err != nil {
log.Fatal("listen err" ,err)
}
}
- 下拉列表 select 如果我们想要判断表单里面<select>元素生成的下拉菜单中是否有被选中的项目。有些时候黑客可能会伪造这个下拉菜单不存在的值发送给你,那么如何判断这个值是否是我们预设的值呢?
我们的select可能是这样的一些元素
代码语言:javascript复制<select name="fruit">
<option value="apple">apple</option>
<option value="pear">pear</option>
<option value="banana">banana</option>
</select>
那么我们可以这样验证,遍历所有的option值,查看是否有相等的
代码语言:javascript复制slice:=[]string{"apple","pear","banana"}
v := r.Form.Get("fruit")
for _, item := range slice {
if item == v {
return true
}
}
return false
- 单选框 Radio 单选框的判断与select相似,要对传递过来的值进行鉴权
<input type="radio" name="gender" value="1">男
<input type="radio" name="gender" value="2">女
代码语言:javascript复制slice:=[]string{"1","2"}
for _, v := range slice {
if v == r.Form.Get("gender") {
return true
}
}
return false
- 复选框 CheckBox:
<input type="checkbox" name="interest" value="football">足球
<input type="checkbox" name="interest" value="basketball">篮球
<input type="checkbox" name="interest" value="tennis">网球
对于复选框我们的验证和单选有点不一样,因为接收到的数据是一个slice,在上面的demo中可以看到,接收值的时候要用r.Form["name"]
方式来接收
slice:=[]string{"football","basketball","tennis"}
a:=Slice_diff(r.Form["interest"],slice)
if a == nil{
return true
}
return false
防 XSS
攻击者通常会在有漏洞的程序中插入JavaScript、VBScript、 ActiveX或Flash以欺骗用户。一旦得手,他们可以盗取用户帐户信息,修改用户设置,盗取/污染cookie和植入恶意广告等。对XSS最佳的防护应该结合以下两种方法:一是验证过滤所有输入数据,有效检测攻击;另一个是对所有输出数据进行适当的处理,以防止任何已成功注入的脚本在浏览器端运行:
代码语言:javascript复制fmt.Println(username) // <script>alert('you have been pwned')</script>
fmt.Println(template.HTMLEscapeString(username)) // 输出到服务器端<script>alert('you have been pwned')</script>!
template.HTMLEscape(w, []byte(username)) // 输出到客户端 <script>alert('you have been pwned')</script>!