goweb 入门
工具
nc 命令
调试热加载
fresh
go get -v -u github.com/pilu/fresh
go install
air
go get -u github.com/cosmtrek/air
处理请求
powershell 中需要使用 curl.exe
RESTful API
通过调用"net/http"和"github.com/gin-gonic/gin"
定义 album 类,实例化
用 router:=gin.Default(),去调用 get/post 和定义的其他方法
通讯问题
- 字符显示问题
chcp 65001
打开 cmd 运行可解决部分错误
- 输入秒发送
逻辑
- server 监听端口
- go 监听消息,将信息发给 usermap
- 接受 socket,处理信息
-
新版客户端 没有存在字符问题 但 rename 和 who 有问题 存疑 client 中的模式处理 user 中的 who 处理
-
44 集踢出问题 在 handle 函数中时间超过后打印被踢信息,并且关闭资源连接,返回 start 函数 但是在 start 函数中一直在 for 循环监听 listener,因此会占用资源 通过排错,发现 server 问题
打印错误
|
|
panic 用法
|
|
for-range channel 用法 当 channel 关闭时,for 循环会自动退出,无需主动检测 channel 是否关闭,防止读取已关闭的 channel,造成读取到数据为通道所存储的数据类型的零值
基础
数据模型
User,session,thread,post 和数据库交互,处理器返回数据给模板引擎,再传给模板
通过 URL 进行接受请求
多路复用器
mux := http.NewServeMux()
net/http,将收到的请求重定向到处理器
将发送至根 URL 的请求重定向到处理器
mux.HandleFunc("/", index)
所有处理器都接受一个 ResponseWriter 和一个指向 Request 结构的指针作为参数,并且所有请求参数通过访问 Requset 结构得到,所以程序不需要向处理器显式地传入任何请求参数
服务静态文件
file := http.FileServer(http.Dir("/public"))
mux.Handle("/static/", http.StripPrefix("/static/", files))
删除/static/字符串在 public 目录里查找被请求的文件
处理器函数,接受 ResponseWriter 和 Request 指针作为参数的函数
cookie 服务器在响应首部写入 cookie,客户端接受 cookie 后把它存储到浏览器里,route_auth.go 的 authenticate 处理器函数
session 记录各项信息存储到数据库,决定访问 public 或 private 页面 utility 包 判断 cookie 存在,data.Session 的 check 方法访问数据库校对唯一 ID 存在,index 函数获取 err 变量判断用户是否已经登录
使用模板生成 HTML 响应 用{{define “”}}标识动作 ParseFiles 函数对模板进行语法分析并创建出相应模板,Must 函数包围 ParseFiles 函数,返回错误报告
(.)代表了传递给被引用模板的数据 {{.Topic}}访问的是 Thread 结构的 Topic 字段,在访问字段时必须在字段名的前面加上点号,并且字段名的首字母必须大写
安装 PostgreSQL
查看数据库 \l
pq 包虽然用_忽略,但实际上连接数据库还是会调用
psql -f setup.sql -d chitchat 无法生效 手动创建数据库
在 chitchat 目录下运行 go build,报 data 包错误,直接将 data 丢入 src 路径,go mod tidy 解决
SSL
处理器与处理器函数
任何实现了 ServeHTTP 的类型都可以作为 HTTP 请求的处理器
处理器拥有 ServeHTTP 方法接口,ServeHTTP 需要接受两个参数,ResponseWriter 和 Request 指针
|
|
允许普通函数直接作为处理器使用,无需定义新类型。
|
|
处理器函数与处理器有相同行为,与 ServeHTTP 方法拥有相同签名
func hello(w http.ResponseWriter, r *http.Requset)
http.HandleFunc("/hello", hello)
HandleFunc 可以把一个带有正确签名的函数 f 转换成一个带有方法 f 的 Handler,并与 DefaultServeMux 进行绑定
处理器函数不能替代处理器,代码包含了某个接口或者某种类型,需要为它们添加 ServeHTTP 方法转变为处理器
串联处理器和处理器函数
ServeMux 请求多路复用器,DefaultServeMux 是 ServeMux 一个实例,用户没有指定处理器则会使用
HTTP2
http2.ConfigureServer(&server, &http2.Server{})
curl -I --http2 --insecure https://localhost:8080/
实际应用在中间件中 通过 HandlerFunc 链式调用实现中间件
|
|
在 Gin 中,处理器函数通常是 gin.HandlerFunc 类型,与标准库思想类似
|
|
表单 enctype 属性
简单使用
application/x-www-form-urlencoded
不同得键值对将使用&符号分隔,键和值用=分隔
first_name=sau%20sheong&last_name=chang
上传文件使用
mutipart/form-data
表单中得键值对带有各自的内容类型和配置
----WebKit.... Content-Disposition:form-data;name="firstname" sau sheong
分析字段
以下三者围绕着返回值和表单属性进行区分 requset 将 url/主体等提取到 form/postform/multipartform 字段中,调用 parseform 方法或者 parsemultipartform 方法进行解析
Form 字段
r.ParseForm()
fmt.Fprintln(w, r.Form)
将获得一个切片,包含键的表单值或者 URL 值
map[thread:[123] hello:[sau sheong world] post:[456]]
PostForm 字段 r.PostForm() 将获得一个键的表单值不包含 URL 值 只支持 urlencoded 编码
application/x-www-form-urlencoded 下 PostForm,获得表单值
map[post:[456] hello:[sau sheong]]
修改成 multipart/form-data 下使用回 r.Form,获得 url 值
map[hello:[world] thread:[123]]
MutipartForm 字段 只包含表单不包含 URL 键值对,包含两个映射,一个为字符串组成切片,一个为空,为上传文件
r.ParseMultipartForm(1024) fmt.Fprintln(w, r.MultipartForm)
&{map[hello:[sau sheong] post:[456]] map[]}
FormValue 字段 允许直接访问与给定键相关联的值 FormValue 自动调用 ParseForm 或 ParseMultipartForm 方法,但指挥从 Form 结构取出给定键的第一个值,要获取全部需要直接访问 Form 结构
PostFormValue 字段 PostFormValue 同理,只会返回表单不会返回 url,两者都只支持 application/x-www-form-urlencoded,在 multipart/form-data 中不会得到任何结果 multipart/form-data 时,数据被存储到 MultipartForm 字段中而不是 Form 字段和 PostForm 字段
上传文件
前端
enctype="multipart/form-data"
<input type="file" name="uploaded">
后端
|
|
执行 ParseMultipartForm 方法,从 MultipartForm 的 File 取出 fileHeader,然后通过调用文件头的 Open 方法打开文件,服务器会将文件的内容读取到一个字节数组中,并将这个字节数组的内容打印出来,纯文本文件会把这个文件内容打印在浏览器上 FormFile 可以返回给定键的第一个值,返回文件和文件头
ParseForm 方法无法从 Angular 客户端发送 POST 请求中获取 JSON 数据,使用的是 application/json,ParseForm 方法只对表单数据进行分析
ResponseWriter 实际上 ResponseWriter 就是 response 这个非导出结构的接口,传递的也是指向 response 结构的指针,包含 Write/WriteHeader/Header 三个方法 Write 可以将 HTML 字符串写入 HTTP 响应主题中 WriteHeader 接受一个代表 HTTP 响应状态码的整数作为参数,并将这个整数用作 HTTP 响应的返回状态码,用户可以继续对 ResponseWriter 进行写入但是不能对响应的首部做任何写入 Header 取得一个由首部组成的映射,修改映射可以修改首部,修改后的首部将被包含在 HTTP 响应里面,并随着响应一同发送至客户端
重定向方法
w.Header().Set("Location", "http://www.baidu.com")
w.WriteHeader(302)
给响应首部添加一个 Location,并设置为重定向目的地,WriteHeader 执行后不再允许写入
Cookie 除了 Expires 字段,还有 MaxAge 字段
设置 cookie
|
|
cookie 实现闪现消息 c := http.Cookie{ Name: “flash”, Value:base64.URLEncoding.EncodeToString(msg) }
rc := http.Cookie{ Name: “flash” MaxAge: -1 Expires: time.Unix(1, 0) } val, _ := base64.URLEncoding.DecodeString(c.Value)
通过完全移除 cookie,程序对旧 cookie 解码并返回
5. 模板引擎
无逻辑模板引擎,将模板中指定的占位符替换成相应的动态数据,完全分离程序的表现和逻辑,计算交给处理器完成
嵌入逻辑的模板引擎,将编程语言嵌入模板并在模板引擎渲染模板时,由代码进行相应的字符串替换
模板中默认动作使用{{}}包围,而点(.)是一个动作,模板引擎执行模板时,使用一个值去替换动作本身 步骤: (1) 对文本格式模板源进行语法分析,创建一个经过语法分析的模板结构,模板源既可以是一个字符串,也可以是模板文件中包含的内容; (2) 执行经过语法分析的模板,将 ResponseWriter 和模板所需的动态数据传递给模板引擎,被调用的模板引擎会把经过语法分析的模板和传入的数据结合起来,生成出最终的 HTML,并将这些 HTML 传递给 ResponseWriter
t, _ := template.ParseFiles("tmpl.html")
t.Execute(w, "Hello World!")
等同于
t := template.New("tmpl.html")
t, _ := t.ParseFiles("tmpl.html')
ParseFiles 可以接受多个文件名作为参数,变成集合,但只返回第一个文件的已分析模板
ParseGlob 会对匹配给定模式的所有文件进行语法分析
t, _ := template.ParseGlob("*.html")
处理分析模板时出现的错误
t := template.Must(template.ParseFiles("tmpl.html"))
Must 函数可以包裹起一个函数,返回一个指向模板的指针和一个错误,如果不是 nil 则产生 panic
panic 会终止正常流程,如果 panic 是内部函数产生那会返回给调用者,一直向调用栈的上方传递,直到 main 函数
如果想执行别的模板需要
t.ExecuteTemplate(w, "t2.html", "Hello World")
动作 条件动作/迭代动作/设置动作/包含动作
t.Execute(w, )负责传入
|
|
|
|
|
|
包含另一个模板 {{template “name”}}
ParseFiles 第一个参数有特殊作用,传递给第二个模板需要用{{template “t2.html” .}},可以把 t1.html 的{{.}}传递给 t2.html
变量$variable := value 管道可以传递给下一个参数
自定义模板函数 (1)创建一个名为 FuncMap 映射并将映射的键设置为函数名字,而映射的值则设置为实际定义函数 (2)将 FuncMap 与模板进行绑定 用户常常需要将时间对象或日期转换为 ISO8601 格式的时间字符串或者日期字符串
定义函数返回 t.Format(layout),处理器中创建一个变量名为 funcMap,使用结构将名字 fdate 映射至 formatDate 函数,template.New 函数创建一个名为 tmpl.html 的模板,funcMap 传递给 template.New 返回被创建模板进行绑定,对 tmpl.html 进行语法分析,将 ResponseWriter 以及当前时间传递给模板
funcMap := template.FuncMap{"fdate": formatDate}
t := template.New("tmpl.html").Funcs(funcMap)
t, _ = t.ParseFiles("tmpl.html")
t.Execute(w, time.Now())
上下文感知,对 HTML/JS 等进行转义
w.Header().Set("X-XSS-Protection", "0")
介于标签之间的内容就是 layout 模板 {{define “layout”}} {{end}}
t.ExecuteTemplate(w, "layout", "")
块动作定义默认模板,可以将 content 放置到 layout 中 {{block arg}} {{end}}
构建运行
1.将文件夹复制到"C:\Program Files\Go\src"中 2.管理员权限开 cmd 运行 go install 目录名 3.可以在 bin 目录里找到 exe,运行
常见问题: 端口被占用无法运行 netstat -ano | findstr 查看端口 taskkill /PID pid /f 关闭端口
curl -i 返回请求头 -d 提交 Post
存储数据
gob 一种能存储在文件里面的二进制格式,可以快速高效地将内存中的数据序列化到一个或多个文件里面
io/ioutill 库 用 WriteFile 和 ReadFile 对文件进行写入读取
|
|
写入程序会将文件的名字/数据以及权限传入 读取将文件名做参,返回一个由字节组成的切片
os 库 通过 File 结构对文件进行写入读取
|
|
Create 出 file 文件后进行写入,Open 文件后进行 read
CSV 处理
创建写入器,把数据创建为一个由字符串组成的切片,Flush 方法保证缓冲区数据写入
|
|
打开 csv 文件,将 FieldsPerRecord 字段设为-1,即使读取时缺少字段也不会中断,为正数时,字段数量少于值会报错,为 0 时,读取第一条记录的字段数量未作值,在使用 readall 一次性读取所有记录
|
|
gob 包
通过二进制读取存储数据 存储数据
|
|
载入数据
|
|
Postgres CRUD
导入驱动,调用库
_ "github.com/lib/pq"
"database/sql"
隐形调用 sql.Register(“postgres”, &drv{})
创建用户,-P 密码,-d 权限
createuser -P -d gwp
创建数据库
createdb gwp
创建表格
psql -U gwp -f setup.sql -d gwp
连接数据库
Db, err = sql.Open(exe, user dbname password sslmode)
如果密码不对,则会产生权限问题,无法对数据库进行操作 defer Db.Close()放在 main 函数中否则占用资源 惰性连接
详细说明 有了 sql.DB 实例之后就可以开始执行查询语句了。
Go 将数据库操作分为两类:Query 与 Exec。两者的区别在于前者会返回结果,而后者不会。
- Query 表示查询,它会从数据库获取查询结果(一系列行,可能为空)。
- Exec 表示执行语句,它不会返回行。
此外还有两种常见的数据库操作模式:
- QueryRow 表示只返回一行的查询,作为 Query 的一个常见特例。
- Prepare 表示准备一个需要多次使用的语句,供后续执行用
Scan 方法把行中的值复制到程序为其提供的参数里面,一般与 QueryRow 搭配使用
查询数据,传入 limit 限制行数
|
|
单一查询
|
|
增加
|
|
删除
|
|
改变
|
|
尝试将其他搜索条件传入,返回 id 值
7.go web
SOAP 响应报文由 WSDL 生成的 SOAP 服务器负责,每次修改服务器,即使是修改返回值类型客户端也需要重新生成
REST 对象模型表示事物,函数称为方法,以资源的形式把模型暴露出来,人们通过少数几个称为动词的动作来操纵资源 HTTP 实现 REST 服务时,URL 用于表示资源,HTTP 方法则操纵资源动词
PUT 再使用时需要知道哪项资源会被替换,POST 则只会创建出一项新资源以及一个新 URL PUT 是幂等的,无论调用多少次服务器的状态都不会改变,PUT 会重复修改一项资源
REST 对资源执行动作方法 把过程具体化,把动作转为名词然后用作资源 把动作用作资源的属性 优点是可以添加额外属性,再用 PATCH 对资源进行部分更新
XML
创建用于存储 XML 数据的结构 使用 unmarshal 将 xml 数据解封
在结构中使用`作为标签,名字必须以大写英文字母开头,创建一个与 XML 元素标签同名的字段存储,可以将元素"</name/>“属性存到字段,也可以使用 a>b>c 形式
解码 处理体积小的 XML 文件,unmarshal
|
|
以流的方式传输 XML 文件以及体积较大的文件 decoder
|
|
创建 XML marshal
|
|
添加 XML 声明
err = ioutil.WriteFile("post.xml", []byte(xml.Header + string(output)), 0644)
encode
|
|
JSON
创建存储结构-把 json 数据解封到结构 创建存储结构-创建用于解码的解码器-遍历整个 JSON 文件并将数据解码至结构
创建存储结构并填充-把结构封装为 JSON 数据 创建存储结构并填充-创建出用于存储 JSON 数据的 JSON 文件-创建用于编码 JSON 数据的编码器-通过编码器把结构编码至 JSON 文件
解码 marshal
|
|
encode
|
|
创建 json marshal
|
|
encode
|
|
并发与并行
并发,多个任务在同一时间段内启动互动,任务通过通信分享数据并协调执行时间,共享一个资源
并行,把大任务分割成小任务,需要独立资源,不会重叠处理
GOMAXPROCS 让并行可以同时运行多个任务
打印数字和英文
创建 go routine 程序
在 TestGoPrint1 中,如果规定 CPU 最大数量不为 1,每次打印的结果不相同,可以通过定义数量来固定结果
go test -run x -bench . -cpu 1
go test -v
需要通过延迟来显示结果
time.Sleep(1 * time.Millisecond)
增加 CPU 的数量并不一定能带来性能提升
等待执行完毕
|
|
如果没有对计数器进行减数操作会引发 panic
通道
只能执行发送操作的字符串管道
ch := make(chan <- string)
只能执行接收操作的字符串管道
ch := make(<-chan string)
阻塞主程序,直到 w <- true 被触发
<-w1
main 函数尝试移除 w1 的值,但没有包含任何值因此阻塞
无缓冲通道会轮番打印,但由于 print 抢先执行,会出现先取后入的情况
有缓冲通道会一直运行直到通道满
select 语句允许从多个通道选择一个来操作,但不加上 default 会出现死锁 当 select 没有发现可用通道,会执行 default,如果不加延时,只会看见默认分支输出,通道 a 和 b 还没来得及接受值,select 就跳过执行
close 关闭通道并不是必须的,用于通知接收者该通道不会在收到任何值
程序从通道取值是多值方式,值和通道的状态
case value, ok1 = <-a
马赛克算法
- 键:图片的文件名,值:图片平均颜色, 通过计算图片每个像素红/绿/蓝 3 种颜色的总和,并将它们除以像素总数量,得到一个三元组,三元组计算图片的平均颜色
- 根据瓷砖图片大小切割目标图片
- 对目标图片的子图片计算位于左上方的第一个像素定义为平均颜色
- 根据子图片平均颜色,在瓷砖图片找出一张最为接近的然后替代。程序需要将子图片平均颜色与瓷砖图片平均颜色转化为三维空间的一个点并计算欧几里得距离
- 选中瓷砖图片后从数据库移除