想了解下区块链相关的东西,从头开始学习go 语法实在是耐不下心,稍微看了下
还是直接做web来学吧,主要材料如下

  • 尚硅谷GoWeb教程
  • go web编程快速入门
  • go语言标准库

web应用的流程如图所示,goweb使用默认的多路服用去转发请求到处理器,如果要使用模板,处理器解析并渲染返回响应,和数据交互通过模型完成

1、简单的hello world应用

不论怎样,首先写个hello world的demo,跑起来再说

package mainimport ("fmt""net/http"
)func main() {http.HandleFunc("/", hello)http.ListenAndServe(":8888", nil)
}func hello(w http.ResponseWriter, r *http.Request) {fmt.Fprint(w, "hello wolrd")
}

一个简单的web页面就启动起来了,相对于javaee的web工程简直太棒了
go语言的标准库放在这里:go标准库

上面的handlerFunc实际上是一个适配器,将func转换为handler
handler是一个接口,实现了handler的ServeHTTP方法,就能够为特定路径提供服务

上面传入的nil实际上是指定了默认的ServeMux,可以自己实现但是没有太大必要

下面自己实现一个handler

func main() {http.HandleFunc("/", hello)myhandler := Myhandler{}http.Handle("/demo", &myhandler)http.ListenAndServe(":8888", nil)
}func hello(w http.ResponseWriter, r *http.Request) {fmt.Fprint(w, "hello wolrd", r.URL.Path)
}type Myhandler struct{}func (h *Myhandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {fmt.Fprint(w, "demo")
}

上面的Myhandler struct时间了Handler接口,因此可以注册到Mux中处理请求了

此外可以通过定义Server结构体自动逸server的各种参数

func main() {myhandler := Myhandler{}server := http.Server{Addr:        ":8888",Handler:     &myhandler,ReadTimeout: 2 * time.Second,}server.ListenAndServe()
}type Myhandler struct{}func (h *Myhandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {fmt.Fprint(w, "server")
}

这个时候还是使用的默认mux,可以创建自己的mux

func main() {mux := http.NewServeMux()mux.HandleFunc("/", hello)myhandler := Myhandler{}mux.Handle("/demo", &myhandler)server := http.Server{Addr:        ":8888",Handler:     mux,ReadTimeout: 2 * time.Second,}server.ListenAndServe()
}func hello(w http.ResponseWriter, r *http.Request) {fmt.Fprint(w, "hello")
}type Myhandler struct{}func (h *Myhandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {fmt.Fprint(w, "server")
}

如果做过web应用那么以上的应该没啥问题,大同小异
这里使用vscode 的rest client插件进行http请求,so easy

2、链接数据库

接下来调试下go链接数据库的的后端
和java一样,golang中的database/sql定义了database的操作,安装相应的驱动即可(这些驱动是按照go的接口标准开发的),sql包统一了database的操作
这里用最常用的mysql数据库来实验,在本地安装好mariadb数据库即可(驱动和mysql貌似都一样,毕竟是亲兄弟)
文档:https://studygolang.com/static/pkgdoc/pkg/database_sql.htm
驱动下载:https://github.com/golang/go/wiki/SQLDrivers

go get github.com/go-sql-driver/mysql

然后再MySQL中创建数据库和表

CREATE DATABASE webdemo;
use webdemo;CREATE TABLE IF NOT EXISTS `student`(`id` INT UNSIGNED AUTO_INCREMENT,`name` VARCHAR(100) NOT NULL,`age` INT,`tele` VARCHAR(40),`birth` DATE,PRIMARY KEY ( `id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO student (name, age, tele, birth) VALUES ("sam", 22, "12304772837",NOW());INSERT INTO student (name, age, tele, birth) VALUES ("bob", 16, "321314431321",NOW());INSERT INTO student(name, age, tele, birth) VALUES ("mary", 22, "7389217391",NOW());

数据库链接工具使用vsvode插件database client,表结构如下

尝试链接并查询studnet表

文档中sql模块中对curd的接口介绍的比较简洁,但是也足够了

Query 查询多行
QueryRow 查询一行
Exec 增删改
Prepare 预编译语句

这里首先获取Db链接

var (Db  *sql.DBerr error
)func init() {Db, err = sql.Open("mysql", "root:zhaojie@tcp(localhost:3306)/webdemo")if err != nil {panic(err.Error())}
}

然后创建User结构,实现crud方法

package modeltype User struct {Id    intName  stringAge   intTele  stringBirth time.Time
}func (u *User) Adduser() error {sqlStr := "insert into student (name,age,tele,birth) values(?,?,?,?)"inStmt, err := dbutils.Db.Prepare(sqlStr)if err != nil {log.Fatal("prepare is wrong", err)return err}u.Birth = time.Now()_, inerr := inStmt.Exec(u.Name, u.Age, u.Tele, u.Birth)if inerr != nil {log.Fatal("insert is wrong", err)return err}return nil
}

为了方便进行测试,看一下go的单元测试方法,参考testing包
测试文件以test_开头,和被测在一个包中,测试函数以Test开头并且参数按照不同的用途进行了分类。
此外如果不以Test开头则默认不执行,可以设置成子测试函数
TestMain可以在测试方法之前执行一段逻辑

package modelfunc TestMain(t *testing.M) {fmt.Println("start testing")
}func TestUser(t *testing.T) {fmt.Println("start test user func")t.Run("test add user", TestAdduser)
}func TestAdduser(t *testing.T) {fmt.Println("start test add user:")user := &User{Name:  "test1",Age:   12,Tele:  "12313213123",Birth: time.Now(),}user.Adduser()
}

在model模块打开控制台运行go test即可,运行之后显示数据库插入成功

$ go test
start testing
start test user func
start test add user:
PASS
ok      beegodemo/model 0.005s

查询操作如下

func (u *User) GetUserByID() (*User, error) {sqlStr := "select * from student where id = ?"row := dbutils.Db.QueryRow(sqlStr, u.Id)var (id        intname      stringage       inttele      stringbirth_str string)err := row.Scan(&id, &name, &age, &tele, &birth_str)if err != nil {return nil, err}DefaultTimeLoc := time.Localbirth, err := time.ParseInLocation("2006-01-02", birth_str, DefaultTimeLoc)user := &User{Id:    id,Age:   age,Name:  name,Tele:  tele,Birth: birth,}return user, err
}

测试方法

func TestGetUserByID(t *testing.T) {fmt.Println("start test get user by id:")user := &User{Id: 2,}u, err := user.GetUserByID()if err != nil {fmt.Println("wrong get user", err)}json, _ := json.Marshal(u)fmt.Print(string(json))
}

测试结果如下

start testing
=== RUN   TestGetUserByID
start test get user by id:
{"id":2,"name":"bob","age":16,"tele":"321314431321","Birth":"2022-01-29T00:00:00Z"}
--- PASS: TestGetUserByID (0.00s)
PASS
ok      beegodemo/model 0.004s

后面的写法大同小异,参考godoc即可
只不过Query方法得到rows,需要便利rows进行scan到每个user对象中

for rows.Next(){rows.Scan(....)user := &User(....)append(users,user)
}

3、处理request请求

直接去看net/http包
requesu是一个struct,根据字段写获取就行

func hello(w http.ResponseWriter, r *http.Request) {fmt.Fprintln(w, r.URL.Path)fmt.Fprintln(w, r.URL.RawQuery)fmt.Fprintln(w, r.Header)fmt.Fprintln(w, r.Header["Accept-Encoding"])fmt.Fprintln(w, r.UserAgent())
}

解析request请求
看一下post请求的解析
request中的Body字段是 io.ReadCloser类型
readcloser有两个子接口reader和closer实现了read和lclose方法
read方法将数据读入byte切片

func (h *Myhandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {length := r.ContentLengthbody := make([]byte, length)r.Body.Read(body)fmt.Fprintln(w, string(body))
}

不知道为什么rest client没有codelens,有点麻烦,懒得查了将就用吧

当然form表单中的数据有专门的获取方式

func form(w http.ResponseWriter, r *http.Request) {r.ParseForm()fmt.Fprintln(w, r.Form)fmt.Fprintln(w, r.PostForm)
}

form字段只有在调用parsefrom之后才有效,并且post请求优先于url查询字符串

注意:postform只支持x-www-form-urlencoded,如果是mutilpart/form-data,需要使用mutilpartform

如果想快速获得某个请求参数那就用formvalue和postformvalue,好处是会自动parseform

上传文件用解析mutilpartform即可,得到一个file指针读取就行了,大同小异

4、给客户端response响应

这部分也比较简单,大体上就是设置响应头和响应体

func resp(w http.ResponseWriter, r *http.Request) {str := `<html><head><title>go web</title></head><body><h1>hello world</h1></body></html>`w.Write([]byte(str))
}

wtiteheader能够设置响应header code,但是调用之后就不能继续修改header了

func resp(w http.ResponseWriter, r *http.Request) {w.WriteHeader(404)fmt.Fprintln(w, "not found")
}

重定向

func resp(w http.ResponseWriter, r *http.Request) {w.Header().Set("Location", "http://bing.com")w.WriteHeader(302)
}

返回json

type Post struct {User    string `json:"user"`Threads []string `json:"threads"`
}func resp(w http.ResponseWriter, r *http.Request) {w.Header().Set("Content-Type", "application/json")post := &Post{User:    "zhaojie",Threads: []string{"first", "second", "third"},}json, _ := json.Marshal(post)w.Write(json)
}

go内置了很多response还是很方便的

  • NotFound
  • ServeFile
  • ServeContent
  • Redirect

5、web模板和模板引擎

模板引擎能够将模板和数据结合起来生成网页

模板引擎有两种

  • 无逻辑,不做逻辑处理,仅仅是占位符替换,完全由handler完成,目标是表示层和罗技完全分离
  • 逻辑嵌入,编程语言嵌入,难以维护

go模板引擎使用了text/template库,介于两种引擎特性之间

简单的模板 demo

<!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>{{ . }}
</body>
</html>

替换占位符

func tmpl(w http.ResponseWriter, r *http.Request) {t, _ := template.ParseFiles("html/index.html")t.Execute(w, "hello world")
}

也可以创建模板集,然后搜寻和使用

写一个模板的demo

func main() {mux := http.NewServeMux()templates := loadTemplates()mux.HandleFunc("/", newtemp(templates))server := http.Server{Addr:        ":8888",Handler:     mux,ReadTimeout: 2 * time.Second,}mux.Handle("/css/", http.FileServer(http.Dir("html/wwwroot")))mux.Handle("/img/", http.FileServer(http.Dir("html/wwwroot")))server.ListenAndServe()
}
//创建模板集
func loadTemplates() *template.Template {result := template.New("templates")template.Must(result.ParseGlob("html/template/*.html"))return result
}
//handler函数
func newtemp(templates *template.Template) func(w http.ResponseWriter, r *http.Request) {temp := func(w http.ResponseWriter, r *http.Request) {filename := r.URL.Path[1:]t := templates.Lookup(filename)if t != nil {err := t.Execute(w, nil)if err != nil {log.Fatalln(err.Error())}} else {w.WriteHeader(http.StatusNotFound)}}return temp
}请求结果和项目结构在下面,注意


有图片和css,效果在网页看比较好
这里有个坑点,注意自己使用的多路复用器是自定义的还是默认的,加载静态文件的时候一定不要弄错了

搭建好了基本的股价,下面来看模板中的action,一共五种

  • 条件
  • 遍历
  • 设置
  • 包含
  • 定义

其实就是大同小异的模板语法,做过web的没啥难度
这里随便写几个demo

func tmplaction(w http.ResponseWriter, r *http.Request) {t, _ := template.ParseFiles("html/template/tmpl.html")rand.Seed(time.Now().Unix())t.Execute(w, rand.Intn(10) > 5)
}

gotemplate-syntax插件

<body>{{ if . }}number os greater than 5{{ else }}number is 5 or less{{ end }}
</body>


模板中还能设置传入的参数,就是让模板的逻辑更加复杂,不过现在都前后端分离了
其他的掠过用到在查,nothing

6、更合理的项目结构

前面的逻辑处理放在了main函数当中,但是实际上main函数应该只负责设置类的工作,将handle罗技全部放在conrtoller中

现在的项目结构如图所示:

用RegisterRoute导出handlefunc

//controller.go
func RegisterRoutes() {//这里还可以加入一些static resourceregisterAboutRoute()registerHomeRoute()registerIndex()
}

下面是未导出的route

func registerHomeRoute() {http.HandleFunc("/test", handleHome)
}func handleHome(w http.ResponseWriter, r *http.Request) {fmt.Fprintln(w, r.URL.Path)fmt.Fprintln(w, r.URL.RawQuery)fmt.Fprintln(w, r.Header)fmt.Fprintln(w, r.Header["Accept-Encoding"])fmt.Fprintln(w, r.UserAgent())
}

如果要解析urlpath中的信息,可以使用regex匹配

还有一些第三方的路由器

  • gorilla/mux,功能多灵活,性能差
  • httprouter,性能号但是功能简单

7、使用middleware中间件

中间件middleware在请求和响应冲handler中进出之前对消息进行处理

middleware实际上也是一个handler,在正式的handler之前形成一个链式结构,逻辑就像下面这样
中间件的用途通常包括

  • logging
  • 安全
  • 请求超时
  • 响应压缩
type MyMiddleware struct{Next http.Handler
}
func(m MyMiddleware)ServeHTTP(w http.ResponseWriter, r *http.Request){//在next handle之前的处理m.Next.ServeHTTP(w,r)//在next handle之后的处理
}

写个dmeo

type AuthMiddleware struct {Next http.Handler
}func (am AuthMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {if am.Next == nil {am.Next = http.DefaultServeMux}auth := r.Header.Get("Authorization")if auth != "" {am.Next.ServeHTTP(w, r)} else {w.WriteHeader(http.StatusUnauthorized)}
}

可以看到没有带authorization头的请求被拦截了,middle将请求和相应参数都传递给next,如果next为空则设置为默认mux

为了在一次绘画过程中保存一些数据,可以使用context环境变量,这个用到在查吧

8、更安全的HTTPS链接

众所周知,https使用tls对通信进行了加密
go可以使用https的linsten进行监听

我们测试环境没有证书,但是好在go为我们提供了脚本生成证书

$ go run /usr/lib/golang/src/crypto/tls/generate_cert.go -h
Usage of /tmp/go-build083634091/b001/exe/generate_cert:-cawhether this cert should be its own Certificate Authority-duration durationDuration that certificate is valid for (default 8760h0m0s)-ecdsa-curve stringECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521-ed25519Generate an Ed25519 key-host stringComma-separated hostnames and IPs to generate a certificate for-rsa-bits intSize of RSA key to generate. Ignored if --ecdsa-curve is set (default 2048)-start-date stringCreation date formatted as Jan 1 15:04:05 2011
$ go run /usr/lib/golang/src/crypto/tls/generate_cert.go -host localhost
2022/01/31 07:52:28 wrote cert.pem
2022/01/31 07:52:28 wrote key.pem

使用上面的两个文件监听即可

func main() {controller.RegisterRoutes()server := http.Server{Addr:        ":8888",Handler:     new(middleware.AuthMiddleware),ReadTimeout: 2 * time.Second,}http.Handle("/css/", http.FileServer(http.Dir("html/wwwroot")))http.Handle("/img/", http.FileServer(http.Dir("html/wwwroot")))server.ListenAndServeTLS("cert.pem","key.pem")
}

https请求的header要复杂很多,具体可以查资料

此外go还支持使用http2协议进行server push

go支持了对于web相应的request测试,这里就不展示了,用到再说

9、提一下性能分析

go自带的性能分析工具还是很强大的,硬起来也很简单
这个貌似目前也用不太到,需要的话转移到
https://www.bilibili.com/video/BV1Xv411k7Xn?p=28&spm_id_from=pageDriver

go web编程入门教程相关推荐

  1. 【转载】PHP面向对象(OOP)编程入门教程

    面向对象编程(OOP)是我们编程的一项基本技能,PHP5对OOP提供了良好的支持. 如何使用OOP的思想来进行PHP的高级编程,对于提高 PHP编程能力和规划好Web开发构架都是非常有意义的.下面我们 ...

  2. PHP面向对象(OOP)编程入门教程

    面向对象编程(OOP)是我们编程的一项基本技能,PHP5对OOP提供了良好的支持.如何使用OOP的思想来进行PHP的高级编程,对于提高 PHP编程能力和规划好Web开发构架都是非常有意义的.下面我们就 ...

  3. 前端如何实现音乐盒胶盘的转动_郑州Web前端入门教程之如何实现图片优化?

    统计数据显示,图片内容已经占据互联网内容总量的62%,因此想要优化网站性能,图片绝对是优化的热点和重点.图片优化是Web前端工程师必须要掌握的知识点,在接下来的郑州Web前端入门教程就给大家讲解一下如 ...

  4. python编程入门-Python编程入门经典pdf(Python编程入门教程) 高清中文版

    Python编程入门经典pdf(Python编程入门教程)下载.Python编程入门经典pdf高清版帮助各位更好的进行Python编程的学习以及理解,最经典的课题,最深入的概念,让你在Python编程 ...

  5. 学习嵌入式的书籍推荐,嵌入式编程入门教程学习大纲

    嵌入式系统是当前热门.具发展前景的IT应用领域之一,很多数字包括手机.电子字典.可视电话.数字相机.数字摄像机.机顶盒.智能玩具医疗仪器和航空航天设备等都是典型的嵌入式系统.越来越多的人想要了解学习嵌 ...

  6. 基于VB的COM编程入门教程

        VB COM编程教程   ⊙ 论坛热贴 用VB编写批量重命名文件问题 如何将图像填充整个表单 正则表达式中如何跳过回车符 如何让小老鼠变个样? >>>>更多... ⊙ ...

  7. python快速编程入门教程-终于懂得python快速编程入门教程

    为了提高模块加载的速度,每个模块都会在__pycache__文件夹中放置该模块的预编译模块,命名为module.version.pyc,version是模块的预编译版本编码,一般都包含Python的版 ...

  8. socket 编程入门教程(三)TCP原理:5、TCP的三次握手(three-way handshake)

    socket 编程入门教程(三)TCP原理:5.TCP的三次握手(three-way handshake) 前面3个小节介绍了socket机制对TCP协议三次握手的实现,需要强调的是,与协议独立于实现 ...

  9. 《Delphi XE6 android 编程入门教程》推荐

    近5.6年已经没有看见关于delphi的新技术的书出来了(看来在国内delphi的使用量确实很低了), 高勇同学最近出了一本<Delphi XE6 android 编程入门教程>,上周刚拿 ...

最新文章

  1. graylog2安装
  2. pandas 排序_懂Excel就能轻松入门Python数据分析包pandas(六):排序
  3. oracle v$ 表,【学习笔记】Oralce视图 查找分析V$PARAMETER视图的基表
  4. jqgrid删除某一列(隐藏)
  5. 【最好的伪原创工具】特别是你的描述中的关键字布局
  6. 信息学奥赛一本通 1064:奥运奖牌计数 | OpenJudge NOI 1.5 07
  7. Android辅助开发工具说明
  8. 项目业务工作笔记001---发改委职责
  9. python怎么清空屏幕_python3.6怎么清屏幕
  10. centos安装后iptables基本设置
  11. keras 双向LSTM 简单示例
  12. emacs之配置代码风格
  13. 5G+北斗高精度定位系统适用于哪些行业领域?
  14. Codechef April Challenge 2019 游记
  15. Unexpected error while obtaining screenshot from device: EOF
  16. 车轮轨迹原理_三张图告诉你倒车入库的原理
  17. OpenCV —— 特征点检测之 SIFT 特征检测器
  18. swarm-XDai主网免bzz质押和rpc全套搭建教程-windows
  19. WPS尾部空格没有下划线
  20. 块级、内联、内联块级

热门文章

  1. Java面试之JVM篇(offer 拿来吧你)
  2. 幽灵行动断点量子计算机图片,【图片】幽灵行动:断点 火山岛全攻略【幽灵行动断点吧】_百度贴吧...
  3. 云客Drupal源码分析之系统AJAX(二):前端原理
  4. 调试经验——OBIEE报表开发实例小结(数据库直连DDR模式、日期型Prompt的设置...)
  5. host文件配置及hostname设置
  6. 虚拟机挂载显示No such file or directory
  7. 基于java的摄影爱好者交流网站
  8. Unity 渲染路径理解
  9. pulse sensor心率传感器
  10. 数据中心低压配电系统能耗分析与PUE计算