数据绑定表单
前面提到对于POST方法,使用PostForm、DefaultPostForm或PostFormArray来获取表单中的键对应的值,如果一个表单中的键值对特别多呢,一个个获取显然代码看起来太笨了,这时候就用到“数据绑定表单”了。
以下是我们这个测试项目的代码路径:
写了一个register.html页面,该页面有一个form表单,长这个样子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="/register" method="post">
<input type="text" name="Username">
<input type="password" name="Password">
<input type="checkbox" name="Hobby" value="GoLang">golang
<input type="checkbox" name="Hobby" value="Python">python
<input type="checkbox" name="Hobby" value="Java">java
<input type="checkbox" name="Hobby" value="C++">cpp
<input type="submit" value="提交">
</form>
</body>
</html>
先用GET(“toRegister”, toRegister)将路由路径localhost:8080/toRegister绑定到处理函数toRegister(),处理函数跳转到一个名为register.html的页面:
func toRegister(c *gin.Context) { //用于跳转到register.html页面
c.HTML(http.StatusOK, "register.html", nil)
}
r.GET("toRegister", toRegister)
来到register.html页面,该页面有一个表单,可以看到表单中有很多键(也就是每个input的name属性),该表单的action为/register,也就是点击提交后,会发出URL为localhost:8080/register的POST请求(这里用的是本地路由),该POST请求中含有表单信息。我们使用POST(“register”, register)来将register()处理函数和localhost:8080/register路由路径绑定。
经过上述操作,当我们访问localhost:8080/toRegister时,返回一个register.html页面,页面中有一个表单,我们填写表单信息,然后提交,会发出一个URL为localhost:8080/register的POST请求,服务器接收到该POST请求后,会调用绑定的register()处理函数,该处理函数中使用ShouldBind()将POST请求中的表单数据和User类绑定,然后User类中的成员变量就被你填写的表单赋值了。
注:成功通过ShouldBind()绑定的前提是User类中的成员名必须和表单中的name属性一模一样。
如果非要不一样也成功绑定的话,就要在User类中每个成员变量后面加上form:"name属性名"
User.go的定义:
package model
type User struct {
Username string
Password string
Hobby []string
}
或者:
package model
type User struct {
Username string `form:"Username"`
Password string `form:"Password"`
Hobby []string `form:"Hobby"`
}
main.go中的几个函数:
package main
import (
"model"
"net/http"
"github.com/gin-gonic/gin"
//"html/template"
)
func toRegister(c *gin.Context) { //用于跳转到register.html页面
c.HTML(http.StatusOK, "register.html", nil)
}
func register(c *gin.Context) {
var user model.User
c.ShouldBind(&user)
c.String(http.StatusOK, "username=%s, password=%d, hobby=%s", user.Username, user.Password, user.Hobby)
}
func main() {
// 1. 创建路由引擎
r := gin.Default()
r.GET("index", someGet)
r.POST("somePost", somePost)
r.GET("getKey", getKey)
r.POST("getPost", getPost)
r.GET("getParam/:username/:age", getParam)
r.POST("search", search)
r.LoadHTMLGlob("../templates/*")
r.GET("toRegister", toRegister)
r.POST("register", register) //toRegister()跳转到register.html页面,在register.html页面填写表单并提交,会发送POST请求,register()处理register.html表单数据
r.Run(":8080")
}
注意看main.go中是如何引入model包的(结合最开始我给出的项目路径看),直接import "model"就可以了,我以为应该是“…/model”,但是“…/model”不对,有人知道为啥吗?
引入静态资源及BootStrap
地址:https://getbootstrap.com/
关键方法:
r.Static(“/页面访问”, “/实际路径”)
r.StaticFile(“/页面访问”, “/实际路径”)
至于“页面访问”和“实际路径”到底是什么,一会儿看代码理解。
什么是BootStrap?
我们在开发前端页面的时候,如果每一个按钮、样式、处理浏览器兼容性的代码都要自己从零开始去写,那就太浪费时间了。所以我们需要一个框架,帮我们实现一个页面的基础部分和解决一些繁琐的细节,只要在它的基础上进行个性化定制就可以了。
Bootstrap 就是这样一个简洁、直观、强悍的前端开发框架,只要学习并遵守它的标准,即使是没有学过网页设计的开发者,也能做出很专业、美观的页面,极大地提高了工作效率。
BootStrap的具体使用
index,html中的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!--这个是自适应各种分辨率的屏幕-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="/assets/css/bootstrap.min.css">
<script src="/assets/js/jquery-3.5.1.min.js"></script>
<script src="/assets/js/bootstrap.min.js"></script>
</head>
<body>
<button type="button" class="btn btn-primary">Primary</button>
<button type="button" class="btn btn-secondary">Secondary</button>
<button type="button" class="btn btn-success">Success</button>
<button type="button" class="btn btn-danger">Danger</button>
<button type="button" class="btn btn-warning">Warning</button>
<button type="button" class="btn btn-info">Info</button>
</body>
</html>
main.go中的代码:
func main() {
// 1. 创建路由引擎
r := gin.Default()
r.Static("/assets", "../assets")
r.StaticFile("/favicon.ico", "../assets/favicon.ico")
r.GET("index", index)
}
这里需要注意的一点是:Static()的第一个参数并不是真正的资源路径,只是第二个参数的代称,是为了简写,需要和html源文件中的资源路径名称一样,具体怎么描述呢,举个例子,如下:
我的项目的目录结构如下:
然后我们的index.html文件这样子引入样式文件:
<link rel="stylesheet" href="../assets/css/bootstrap.min.css">
<script src="../assets/js/jquery-3.5.1.min.js"></script>
<script src="../assets/js/bootstrap.min.js"></script>
为了更明显,我们将main.go中的Static()改成r.Static(“/abc”, “…/assets”):
func main() {
// 1. 创建路由引擎
r := gin.Default()
r.Static("/abc", "../assets")
r.StaticFile("/favicon.ico", "../assets/favicon.ico")
r.GET("index", index)
}
我们可以看到,index.html中的样式资源路径命名是对的,我们清除浏览器的cookie,再访问一次localhost:8080/index,会发现显示出来的按钮并没有CSS样式,看起来这个三个样式文件并没有加载成功。为什么呢,样式资源它是怎么查找的呢?
我们按照Static()函数中的内容,把index.html改成下面这样:
<link rel="stylesheet" href="/abc/css/bootstrap.min.css">
<script src="/abc/js/jquery-3.5.1.min.js"></script>
<script src="/abc/js/bootstrap.min.js"></script>
然后清除浏览器数据,再访问localhost:8080/index,发现样式成功显示了,也就是找到资源了。这说明:服务器在找样式资源的时候,找的是Static()函数的第二个参数指定的路径,第一个参数只是第二个参数的代称,写成什么都可以,比如我们这里写成abc,然后把index.html中也要相应地改成/abc开头的,这样再查找时,服务器先把abc替换成…/static,然后再按替换后的路径找资源。
最后,成功的页面长这个样子:
一些小技巧
前面我们提到,Static中的第一个参数是第二个参数的代称,html中的资源目录需要和它一样,但是注意到**/abc/css/bootstrap.min.css这种形式的资源目录在写代码时无法用ctrl+鼠标左键访问,而且写的时候也没有提示,很不爽,那么我们可以把Static的第一个参数写成像…/assets这个样子,这样的话html中的资源目录就要对应地改成…/assets/css/bootstrap.min.css**,这样岂不是既有提示,又可以用ctrl+鼠标左键访问资源,服务器又能成功找到资源,一举多得。