goWeb学习笔记完整版
发布日期:2021-06-22 10:30:59 浏览次数:4 分类:技术文章

本文共 10231 字,大约阅读时间需要 34 分钟。

背景

整理一下上个月学习用go语言搭建服务器的笔记。

本文基于go1.14,文本编辑器采用vscode1.43.2,如果对go语言不甚了解,可以参见我的博客

监听端口号,响应客户端请求

package mainimport (    "fmt"    "net/http")func handler(w http.ResponseWriter, r *http.Request) {    // 处理请求函数,函数名任意,参数固定    fmt.Fprintln(w, "Hi everyone", r.URL.Path)    // 向w输入后面的参数}func main() {    http.HandleFunc("/", handler)    // 注册处理函数,传入路径(根目录)和对应处理器    http.ListenAndServe(":8080", nil)    // 创建路由,传入端口号和多路复用器(nil表示默认)}

ListenAndServe()函数的第二个参数为传入的多路复用器,多路复用器接收用户请求根据url,来判断用哪个处理器来处理请求。

编译运行

PS D:\develop\Microsoft VS Code\workspace> go build -o web.exe go_code/go_web/mainPS D:\develop\Microsoft VS Code\workspace> .\web.exe

然后打开localhost:8080,进行验证

访问根目录

访问/next

也可以通过实现ServeHttp方法的方式,来处理请求,方法如下

package mainimport (    "fmt"    "net/http")type myHandler struct{}func (my_handler *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {    fmt.Fprintln(w, "Hi from struct server", r.URL.Path)}func main() {    http.Handle("/", &myHandler{})    // 注册处理函数,传入路径(根目录)和对应处理器    http.ListenAndServe(":8080", nil)    // 创建路由,传入端口号和多路复用器(nil表示默认)}

还可以通过实例化http.Server的方式进行初始化服务器

func main() {    server := http.Server{        Addr: ":8080",        Handler: &myHandler{},        ReadHeaderTimeout: 2 * time.Second,    }    server.ListenAndServe()}

导入time包即可

import (    "fmt"    "net/http"    "time")

使用自己的多路复用器,用的很少

mux := http.NewServeMux()        mux.Handle("/", &myHandler{})    http.ListenAndServe(":8080", mux)

获取客户端请求相关信息

func (my_handler *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {    fmt.Fprintln(w, "Hi from struct server", r.URL.Path)    fmt.Fprintln(w, "header", r.Header) //头部信息    fmt.Fprintln(w, "method:", r.Method) // 请求方法    fmt.Fprintln(w, "RawQuery:", r.URL.RawQuery) // GET方法的请求参数}

浏览器输入 后,浏览器输出如下

 可见header是一个map[string][]string类型,可以根据映射取值方式来获取想要的内容

fmt.Println("header中的accept-encoding:", r.Header["Accept-Encoding"])    fmt.Println("header中的浏览器:", r.Header["User-Agent"])

输出如下

header中的accept-encoding: [gzip, deflate, br]header中的浏览器: [Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36]

获取POST方法的请求体时,要先构造byte切片,再通过r.Body.Read()方法进行获取

body := make([]byte, r.ContentLength)    r.Body.Read(body)    fmt.Fprintln(w, "body:", string(body))

构造以下html文件,向localhost:8080/info路径发出请求

            
用户名:
密码:

用浏览器打开,输入以下内容

提交后的输出如下所示

获取表单信息

 

err = r.ParseForm() // 必须先调用ParseForm()方法    if err != nil {        fmt.Println("Parse form error:", err)        return    }    fmt.Println(r.Form) // 表单 + url中参数,如果有同名,表单中的值靠前    fmt.Println(r.PostForm) // 仅表单

对应html如下

            
用户名:
密码:

用浏览器打开,输入以下内容

提交后控制台输出如下

map[k1:[v1] k2:[v2] password:[pass] username:[user]]map[password:[pass] username:[user]]

注意,如果之前调用了r.Body().Read(),那么这里只能获取空映射

如果form的enctype是multipart/form-data的话,就要调用MultipartForm()来获取映射了

也可以调用FormValue()和PostFormValue()直接获取表单中的值,这两个方法必要时会调用ParseForm()方法

fmt.Println(r.FormValue("k1"))    fmt.Println(r.PostFormValue("username"))

输出如下

v1user

响应客户端请求

直接响应请求

主要是向w里写数据,之前的fmt.FPrintln()就可以。如果要向客户端写入json数据,就要先导入encoding/json包,然后给w设置header,最后写入json字节切片

import (    "encoding/json"    ...)type User struct {    Id       int    `json:"id"`    Username string `json:"username"`    Password string `json:"password"`}func (my_handler *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {    ...    w.Header().Set("Content-Type", "application/json") // 设置header    user := User{        Id:       1,        Username: "szc",        Password: "pwd",    }    json, _ := json.Marshal(&user) // 解析json    w.Write(json)}

输出结果如下

如果要在响应中重定向到其他网址,可以使用以下代码,注意不要再重定向前进行其他响应

func (my_handler *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {    w.Header().Set("Location", "https://www.baidu.com")    w.WriteHeader(302)}

在浏览器输入localhost:8080后,就会跳转到百度首页,并且在浏览器控制台,会找到我们的响应报文

利用模板响应请求

 从模板中响应请求

import (    ...    "html/template"    ...)func (my_handler *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {    t, _ := template.ParseFiles("output.html") // 解析模板文件    t.Execute(w, "Response from template") // 执行模板引擎,传入数据}

模板文件output.html内容如下,{

{}}是模板动作,来接收响应数据,.表示所有数据

            
后台传来的数据:{
{.}}

浏览器输出如下

如果要传入多个模板文件,就要调用ExecuteTemplate()方法,传入指定输出的模板文件名以及要输出的数据

t, _ := template.ParseFiles("output.html", "output_1.html") // 解析多个模板文件    t.ExecuteTemplate(w, "output_1.html", "Response from template to html2") // 参数列表:writer、输出模板文件、输出数据

模板文件output_1.html内容如下

            
后台传来的数据:{
{.}} (html1)

浏览器输出如下

模板动作

以下是一些模板引擎中的动作语句

分支选择

            
后台传来的数据: {
{if .}} select1 {
{else}} select 2 {
{end}} (html)

后台输出测试如下

func (my_handler *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {    t, _ := template.ParseFiles("output.html")    t.Execute(w, 2 > 1)}

浏览器输出如下

遍历动作

遍历并输出某个元素的指定字段

            
后台传来的数据: {
{range .}} {
{if .}}
当前元素:{
{.Username}} {
{end}} {
{else}} nothing {
{end}}
(html)

后台输出测试如下

func (my_handler *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {    t, _ := template.ParseFiles("output.html")    slice_ := make([]*User, 5)    slice_ = append(slice_, &User{Id: 0, Username: "user0", Password: "pwd0"})    slice_ = append(slice_, &User{Id: 1, Username: "user1", Password: "pwd1"})    slice_ = append(slice_, &User{Id: 2, Username: "user2", Password: "pwd2"})    slice_ = append(slice_, &User{Id: 3, Username: "user3", Password: "pwd3"})    slice_ = append(slice_, &User{Id: 4, Username: "user4", Password: "pwd4"})    t.Execute(w, slice_)}

浏览器输出如下

遍历元素时,也可以输出整个元素,如下

            
后台传来的数据: {
{range .}} {
{if .}}
当前元素:{
{.}} {
{end}} {
{else}}
nothing {
{end}}
(html)

浏览器输出如下

设置动作

设置动作用with就可以,但是如果with传入的字符串为空,就不会进行设置

            
后台传来的数据:{
{.}}
尝试改变数据 {
{with "newData"}} 新数据:{
{.}} {
{end}}
{
{with ""}} 传入""后的结果:{
{.}} {
{else}} 空字符串没有修改: {
{.}} {
{end}}
(html)

后台代码如下

func handler(w http.ResponseWriter, r *http.Request) {    t, _ := template.ParseFiles("output.html")    t.Execute(w, "后台数据")}

浏览器的输出如下

包含动作

使用template动作把另一个模板文件里的内容拷贝进来。

output.html模板内容如下

            
后台传来的数据:{
{.}}
output1.html里的数据:
{
{template "output_1.html"}}
{
{template "output_1.html" .}}
(html)

output_1.html模板内容如下

            
后台传来的数据:{
{.}} (html1)

后台处理逻辑如下

func handler(w http.ResponseWriter, r *http.Request) {    t, _ := template.ParseFiles("output.html", "output_1.html")    t.Execute(w, "后台数据")}

浏览器输出如下

可见output.html中的两个模板动作里,上面的仅仅拷贝了模板,下面连同数据也引入了

定义动作

可以使用define去定义一个新动作

{
{define "model"}}
后台传来的数据:{
{.}} (html1)
{
{template "content"}}
{
{end}}{
{define "content"}} 百度{
{end}}

后台使用ExecuteTemplate()方法指定加载的动作和参数

func handler(w http.ResponseWriter, r *http.Request) {    t, _ := template.ParseFiles("output_1.html")    t.ExecuteTemplate(w, "model", "")}

浏览器输出如下

我们还可以根据不同的情况加载不同的模板文件,从而加载不同的动作,哪怕重名。

现有output.html和output_1.html,唯一不同的是前者把百度改成了新浪,故而只贴出后台逻辑

func handler(w http.ResponseWriter, r *http.Request) {    var t *template.Template    if strings.Contains(r.URL.Path, "_1") {        t, _ = template.ParseFiles("output_1.html")    } else {        t, _ = template.ParseFiles("output.html")    }    t.ExecuteTemplate(w, "model", "")}

浏览器输入 ,浏览器输出如下

而的输出如下

块动作

当后台执行模板时,模板没有对应的动作,就可以使用block定义默认的动作

output.html内容如下,当没有给"content"指定动作时,使用block进行默认的输出

{
{define "model"}}
后台传来的数据:{
{.}} (html1)
{
{block "content" .}} block {
{end}} {
{end}}

而"content"动作定义在output_1.html中

{
{define "model"}}
后台传来的数据:{
{.}} (html1)
{
{template "content"}} {
{end}}{
{define "content"}} 百度{
{end}}

后台处理逻辑如下

func handler(w http.ResponseWriter, r *http.Request) {    var t *template.Template    if strings.Contains(r.URL.Path, "_1") {        t, _ = template.ParseFiles("output.html", "output_1.html")    } else {        t, _ = template.ParseFiles("output.html")    }    t.ExecuteTemplate(w, "model", "")}

当输入url包含_1时,加载两个模板文件,这时"content"的定义在output_1.html中被找到,所以会加载出相应内容

当输入url不含_1是,只加载一个模板文件,没有"content"的定义,所以加载block中默认的内容

处理静态资源

go语言不能直接访问css、js等静态资源,需要我们为这些资源的请求注册处理函数

http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(utils.Static_res))))    // 处理静态资源,入参:请求路径,处理器。    http.Handle("/pages/", http.StripPrefix("/pages/", http.FileServer(http.Dir(utils.Pages))))

Handle()方法里的/static/表示要处理的请求相对路径,也就是静态css和js所在的目录,后面的http.FileServer()返回一个处理器,这里用来访问静态资源所在目录。

http.FileServer的入参就是目录的路径,这里使用自定义常量来表示

var (	Project_src = "D:/develop/Go/workspace/src/go_code/book_store/src/"	Index_html  = Project_src + "views/index.html"	Pages      = Project_src + "views/pages/"	Static_res = Project_src + "views/static/")

cookie

设置cookie时,可以使用Set或Add方法

func setCookie(w http.ResponseWriter, r *http.Request) {    cookie := http.Cookie{        Name:  "user2",        Value: "admin2",    }    http.SetCookie(w, &cookie)}

或者

func setCookie(w http.ResponseWriter, r *http.Request) {    cookie := http.Cookie{        Name:  "user2",        Value: "admin2",    }    w.Header().Set("Set-Cookie", cookie.String())    w.Header().Add("Set-Cookie", cookie.String()) }

所有Set方法都会覆盖老的,Add方法则不会

除了键值对,也可以为Cookie指定有效期限,单位是秒

cookie2 := http.Cookie{        Name:   "user",        Value:  "admin2",        MaxAge: 60,    }

获取cookie时,可以从头部信息获取全部的cookie,也可以获取指定的cookie

获取header中的所有cookie,返回的是一个string切片

cookies := r.Header["Cookie"]

获取指定cookie,返回的是一个Cookie指针

cookie_, _ := r.Cookie("user")    cookie_.String()

结语

go语言搭建服务器还是很简单的,不用安装任何其他包,就可以进行端口的监听、请求的响应等操作。

转载地址:https://blog.csdn.net/qq_37475168/article/details/105454110 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:CentOS安装docker
下一篇:go语言基础学习笔记完整版

发表评论

最新留言

很好
[***.229.124.182]2024年03月22日 20时20分36秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

冰点下载器手机版apk_冰点文库下载器 2019-04-21
python信号采集代码_13行代码实现:Python实时视频采集(附源码) 2019-04-21
h5引入json_纯js直接引入json文件 2019-04-21
python格式化字符串总结_Python字符串处理方法总结 2019-04-21
python中true什么意思_python中的bool是什么意思 2019-04-21
jacobian 矩阵意义_Jacobian矩阵和Hessian矩阵的作用是什么? 2019-04-21
c++ jna 数据类型_JNA 使用总结 2019-04-21
python中如何遍历列表并将列表值赋予_python中如何实现遍历整个列表? 2019-04-21
apache php mysql架构图_Apache+PHP+MYSQL+Tomcat+JK架构设计技巧与应用实战 2019-04-21
xlnt库如何编译_最新mysql数据库源码编译安装。 2019-04-21
mysql 2003错误 10055_MYSQL无法连接---提示10055错误 2019-04-21
mysql redis缓存层_redis实现缓存的两种方式 2019-04-21
git 改local branch名字_用Git管理Latex写论文的工作流程 2019-04-21
mysql索引篇_MySQL索引篇 2019-04-21
有至少一个用MySQL_Mysql有用的面试题 2019-04-21
mysql select同时update_MySQLSELECT同时UPDATE同一张表 2019-04-21
mysql删除后数据库没变化_mysql之delete删除记录后数据库大小不变 2019-04-21
net mysql start3534_MySQL 5.7.14 net start mysql 服务无法启动-“NET HELPMSG 3534” 的奇怪问题... 2019-04-21
pta两个有序链表的合并_7-1 两个有序链表序列的合并 (20分) --- 内存问题再叙 2019-04-21
python问题描述怎么写_python写文件有时候写不进去怎么办 2019-04-21