前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布

GoWeb

作者头像
h3110_w0r1d
发布2024-02-19 20:15:50
1120
发布2024-02-19 20:15:50
举报
文章被收录于专栏:用户9703952的专栏

搭建服务器

```go package main

import (

代码语言:javascript
复制
  "fmt"
  "net/http"

)

//创建处理器函数 func handler(w http.ResponseWriter,r*http.Request) { //这里面的参数是不能变的

代码语言:javascript
复制
  fmt.Fprintln(w,"Hello world","abc",r.URL.Path,"def") //fprintln函数可以随意拼接自己想要的字符

}

func main() {

代码语言:javascript
复制
  http.HandleFunc("/abc",handler) //定义一个函数类型,就可以把函数作为参数传入,handlerfunc函数当访问根目录时就会自动执行handler函数
                                  //handlerfunc函数会将指定的url拼接到后面,当拼接了指定的url时,会自动执行handler函数
  //创建路由
  http.ListenAndServe(":8088",nil) //ListenAndServer函数会映射指定的端口,第一个参数就是映射到哪个端口,第二个参数是
                                   //ListenAndServer函数需要传入两个参数都需要监听的端口和handler,第一个是监听的端口,第二个是处理请求的接口,

}

代码语言:javascript
复制
2.   handler函数:是一个接口,接口名随便起,参数是固定的,必须是w http.ResponseWriter 和 r*http.Request,自定义的,所以不需要写http.包的名字

3.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221014212121.png)

4.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221014212749.png)

5.   HandlerFunc,是调用http包的函数,所以必须调用http.来说明是包内的函数,第一个参数是后面拼接的后缀(映射的地址)(url为string类型),第二个参数是一个处理器,说明映射的地址交给哪个处理器去完成

6.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221014212520.png)

7.   ListenAndServe函数第一个参数是监听TCP地址addr(端口),并且会使用handler参数调用Serve函数处理接收到的链接,handler参数一般设置为nil,此时会使用DefaultServeMux

8.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221014213351.png)

1.   ```go
     package main
     
     import (
     	"fmt"
     	"net/http"
     )
     
     type MyHandler struct {}
     
     func (m *MyHandler) ServeHTTP(w http.ResponseWriter , r *http.Request) {
     	fmt.Fprintln(w,"通过自己创建的处理器处理请求!")
     }
     
     func main() {
     	myHandler := MyHandler{}
     	http.Handle("/myHandler",&myHandler) //Handle函数的第二个参数是myHandler的地址,实例对象的地址
     
     	http.ListenAndServe(":8088",nil)
     }
     //这个是比较麻烦的自己创建处理器的方法,不推荐使用

如果调用的是HandleFunc函数会自动转换成处理器,不需要一个struct来实现接口

如果调用的是Handle函数,则必须要实现ServeHTTP方法(一个接口)

HTTP报文格式

请求报文

``` 第一行是请求首行,包含请求方式,请求地址 和 请求协议 第二行开始是请求头信息,就是请求的属性信息 后面紧跟一个空行 空行后面是请求体

代码语言:javascript
复制
5.   get请求没有请求体,post请求才有请求体

6.   可以通过浏览器的network来查看报文信息,其中view-source选项可以查看具有报文结构的报文信息

7.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221015113803.png)

8.   ```go
     package main
     
     import (
     	"fmt"
     	"net/http"
     )
     
     //创建处理器函数
     func handler(w http.ResponseWriter , r *http.Request){
     	fmt.Fprintln(w , "测试http协议")
     }
     
     func main() {
     	//调用处理器处理请求
     	http.HandleFunc("/http",handler)
     	//路由
     	http.ListenAndServe(":8080",nil)
     }

```html

用户名: 密码:

代码语言:javascript
复制
10.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221102110231.png)

# 通过go语言链接数据库

1.   查看如何拼接sql语句官方文档看database/sql库中的
2.   [Go语言标准库文档中文版 | Go语言中文网 | Golang中文社区 | Golang中国 (studygolang.com)](https://studygolang.com/pkgdoc)
3.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221015164640.png)
4.   使用database/sql包来操作数据库
5.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221015164950.png)
6.   因为go语言没有提供任何官方的数据库驱动,所以我们需要导入第三方的数据库驱动,“github.com/go-sql-driver/mysql”
7.   放在 gomodcache 指向的目录下,有一个github.com目录
8.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221015165822.png)
9.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221015165843.png)
10.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221015170131.png)
11.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221015170715.png)
12.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221015170727.png)

## 链接数据库

1.   ```go
     package utils
     
     import (
     	"database/sql"
     	_ "github.com/go-sql-driver/mysql"
     )
     
     var (
     	Db *sql.DB
     	err error
     )
     
     func init() {
     	Db,err := sql.Open("mysql","root:20030729a@tcp(localhost:3306)/ctfshow")
     	if err != nil {
     		panic(err.Error())
     	}
         DB.SetConnMaxLifetime(10)
     	DB.SetMaxIdleConns(5)
         if err := DB.Ping() ; err != nil {
     		fmt.Println("open database fail")
     		return
     	}
     }

adduser添加用户的方法

代码语言:javascript
复制
func (user *User) AddUser() error {
	//写sql语句
	sqlStr := "insert into users(username,password,email) values(?,?,?)"
	//预编译
	inStmt , err := utils.Db.Prepare()
}

Exec执行一次命令,(包括查询,删除,更新,插入等),不返回任何执行结果,参数args表示query中的占位参数

代码语言:javascript
复制
func (db *DB) Exec(query string,args ...interface()) {Result,error}

Query执行一次查询,返回多行结果,(即Rows),一般用于执行select命令,参数args表示query中的占位参数

代码语言:javascript
复制
func (db *DB) Query(query string,args ...interface()) {*Rows,error}

QueryRow执行一次查询,并期望返回最多一行结果(即Row),QueryRow总是返回非nil的值,查到返回值的Scan方法被调用时,才会返回被延迟的错误

代码语言:javascript
复制
func (db *DB) QueryRow(query string,args ...interface()) *Row

总:AddUser 添加User的方法一,需要预编译

代码语言:javascript
复制
// AddUser 添加用户的方法一
func (user *User) AddUser() error {
	//写sql语句
	sqlStr := "insert into users(username,password,email) values(?,?,?)"
	//预编译
	inStmt , err := utils.Db.Prepare(sqlStr) //预编译得到的是inStmt,通过操作inStmt得到不同的结果
	if err != nil {
		fmt.Println("预编译出现异常",err)
		return err
	}
	//3.执行
	_,err2 := inStmt.Exec("admin","123456","admin@atguigu.com")
	if err2 != nil {
		fmt.Println("执行出现异常",err2)
		return err2
	}
	return nil
}

单元测试

单元测试就是为了验证单元的正确性而设置的自动化测试,一个单元就是程序中的一个模块化部分

一般来说,一个单元通常会和程序中的一个函数或者一个方法对应

go的单元测试需要用到testing包以及go test命令,而且对测试文件也有以下要求

```

被测试的源文件和测试文件必须位于同一个包下

测试文件必须以 _test.go结尾

虽然go对测试文件_test.go的前缀没有强制要求,不过一般都设置魏被测试文件的文件名,对user.go测试,名字一般设置为user_test.go

测试文件中的测试函数为 TestXXX(*test.T) 其中,XXX的首字母必须是大写的英文字母 函数参数必须是test.T的指针类型

Test测试函数的参数必须是 t *test.T

代码语言:javascript
复制
5.   ```go
     package model
     
     import (
     	"fmt"
     	"testing"
     )
     
     func TestAddUser(t *testing.T) {
     	fmt.Println("测试添加用户:")
     	user := &User{}
     	//调用添加用户的方法
     	user.AddUser()
     	user.AddUser2()
     }

如果函数名不是以Test开头,那么函数默认不执行,我们可以将它设置成为一个子测试程序

在主Test函数中调用子测试程序,可以将声明的test.T指针对象指向子测试函数

```go t.Run(“测试添加用户”,testAddUser) //第一个参数是自己写的string类型,在调用时自动输出,第二个参数是要调用哪个子测试程序

代码语言:javascript
复制
9.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221102193939.png)

## 预处理SQL

1.   预编译语句是将需要反复调用的某一条sql语句的值用占位符代替,可以视为将sql语句模板化或者参数化,这类语句即为prepared statements,预编译语句

2.   优势在于:一次编译,多次运行,省去了解析优化的过程,此外预编译语句能够防止sql注入

3.   ```sql
     # 定义预处理语句
     prepare state_string from prepareable_state_ment;
     # 执行预处理语句
     execute state_string [using @var_name , @var_name]
     # 删除(释放)定义
     {deallocate|drop} prepare state_ment;

处理一条语句

QueryRow函数返回的是一行结果的指针,row的内容隐藏或非导出字段,代表单行查询结果

实际查询代码

```go func (user *User) GetUserById() (*User,error) {

代码语言:javascript
复制
  //写sql语句
  sqlStr := "select id,username,password,email from users where id = ?"
  //执行
  row := utils.Db.QueryRow(sqlStr,user.ID)
  //声明
  var id int
  var username string
  var password string
  var email string
  err := row.Scan(&id,&username,&password,&email)
  if err != nil {
      return nil,err
  }
  u := &User {
      ID : id,
      Username : username,
      Password: password,
      Email: email,
  }
  return u , nil

}

代码语言:javascript
复制
6.   单元测试代码

7.   ```go
     //测试获取一个User
     func testGetUserById(t *testing.T){
     	fmt.Println("测试一条查询数据")
     	user := User {
     		ID : 3,
     	}
     	//调用获取User的方法
     	u,err2 := user.GetUserById()
     	if err2 != nil{
     		fmt.Println(err2)
     	} else {
     		fmt.Println("得到的User信息是:",u)
     	}
     }

处理多条语句

实际查询代码

```go //GetUsers 获取数据库中的所有记录 func (user *User) GetUsers() ([]*User,error) { //在一个切片上存储指针或带指针的值,典型的例子是[]*string

代码语言:javascript
复制
  //写sql语句
  sqlStr := "select id,username,password,email from users"
  //执行
  rows ,_ := utils.Db.Query(sqlStr)
  // if err3 != nil {
  //     return err3,nil
  // }

  //创建User切片
  var users []*User
  for rows.Next(){
  var id int
  var username string
  var password string
  var email string
  err := rows.Scan(&id,&username,&password,&email)
  if err != nil {
      return nil,err
  }


  u := &User { //要被添加的值是地址
      ID : id,
      Username : username,
      Password: password,
      Email: email,
  }
  users = append(users,u) //append函数的第一个值是被添加到的slice的地址,第二个值是添加的值,因为这个地方是一个存储地址的数组,所以添加的值也是地址
  }
  return users,nil

}

代码语言:javascript
复制
4.   单元测试代码

5.   ```go
     //测试获取所有的User
     func testGetUsers(t *testing.T){
     	fmt.Println("测试查询所有记录:")
     	user := &User{}
     	//调用获取所有User的方法
     	rows,_ := user.GetUsers()
     	//遍历输出切片中的内容
     	for k , v := range rows{
     		fmt.Printf("第%v个用户是%v:",k+1,v)
     		fmt.Println()
     	}
     }

处理请求

切片上存储指针或带指针的值

  1. 在一个切片上存储指针或带指针的值,典型的例子是[]*string

获取请求行中的信息(url和传递的参数)

这个地方的Request类型和handler处理器中的参数是一个类型

其中Request数据类型中的URL属性,也是一个结构体type

```go package main

import (

代码语言:javascript
复制
  "fmt"
  "net/http"

)

//创建处理器函数 func handler(w http.ResponseWriter,r *http.Request){

代码语言:javascript
复制
  fmt.Fprintln(w,"你发送的请求地址是:",r.URL.Path)
  fmt.Fprintln(w,"你发送的请求地址后的查询字符串是:",r.URL.RawQuery)

}

func main(){

代码语言:javascript
复制
  http.HandleFunc("/hello",handler)

  http.ListenAndServe(":8080",nil)

}

代码语言:javascript
复制
7.   Request类型的变量r,r.URL.Path获得到的是请求地址,r.URL.RawQuery获得到的是传递的参数

## 获取请求行和请求体

1.   Request类型中的Header字段即为请求头

2.   Request变量r中的Header字段,代表了请求头中的所有信息

3.   如果想要获取Header字段中的某个信息,可以通过中括号取下标的方式来获取

4.   ```go
     r.Header["Accept-Encodeing"] //这样可以获取请求头中的报文编码格式

获取Header字段中某个信息的属性值用Get方法

```go r.Header.Get(“Accept-Encoding”)

代码语言:javascript
复制
7.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221103114058.png)

8.   Get返回键对应的第一个值,如果键不存在会返回"",如果获取该键对应的值切片,请直接用规范格式的键访问map

9.   ```go
     package main
     
     import (
     	"fmt"
     	"net/http"
     )
     
     //创建处理器函数
     func handler(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,"请求头中Accept-Encoding的信息是:",r.Header["Accept-Encoding"])
     }
     
     func main(){
     	http.HandleFunc("/hello",handler)
     
     	http.ListenAndServe(":8080",nil)
     }

其中不同的是:r.Header[属性]获取到的是map

r.Header.Get()获取到的是值,没有大括号,Get函数中的参数是string类型的属性,和大括号取值中大括号中的内容相同

refer属性

  1. Referer属性可以起到防盗链和广告计费的作用,防盗链:如果不是特定的页面跳转过来的权限不是完全开放;广告计费:在页面中加广告,当从特定的Referer跳转过来的时候,访问流量达到多少会计费

获取请求体中的信息

  1. 请求和响应的主体都是由Request结构中的Body字段表示,这个字段是io.ReadClose接口

获取请求参数

通过net/http库中的Request结构的字段以及方法获取请求URL后面的query参数和POST或PUT的表单数据

如果想要获取postform字段中的数据,需要特定enctype的属性值为application/x-www-form-urlencoded,指定编码方式,如果编码方式为multipart/form-data的属性值,则使用postform字段无法获取表单中的数据

form表单的enctype属性的默认值时application/x-www-form-urlencode编码,实现文件上传时需要将该属性的值设置为multipart/form-data的编码格式

Request的type中,ContentLength属性记录相关内容的长度,在客户端,如果Body非nil而该字段为0,则表示不知道Body的长度

```go //获取请求体中内容的长度

代码语言:javascript
复制
  len := r.ContentLength //ContentLength属性在Request对象中
代码语言:javascript
复制
11.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221103171021.png)

12.   将Body中的内容读到body中

13.   ```go
      //将Body中的内容读到body中
      	r.Body.Read(body)

通过直接调用FormValue方法和PostFormValue方法直接获取请求参数的值

  1. FormValue函数传递的参数是键key,string类型的参数
  1. From是解析好的表单数据,包括URL的query参数和POST或PUT传递的表单数据

处理客户端响应

handler处理器的第一个参数,w http.ResponseWriter类型的对象w

例子

```go func handler(w http.ResponseWriter , r *http.Request){

代码语言:javascript
复制
  w.write([]byte("你的请求我已经收到"))

}

代码语言:javascript
复制
6.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221103174031.png)

7.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221103174806.png)

### 给客户端响应改变为json格式

1.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221103184358.png)

2.   记住,一定要**导入encoding/json的包**

3.   ```go
     func testJsonRes(w http.ResponseWriter,r *http.Request){
     	//设置响应内容的类型
     	w.Header().Set("Content-Type","application/json")
     	//创建User
     	user := model.User{
     		ID:1,
     		Username:"admin",
     		Password:"123456",
     		Email:"admin@atguigu.com",
     	}
     	//将User转换为Json格式
     	json,_ := json.Marshal(user)
     	//将json格式的数据相应给客户端
     	w.Write(json)
     }
     
     func main(){
     	http.HandleFunc("/hello",handler)
     	http.HandleFunc("/testJson",testJsonRes)
     
     	http.ListenAndServe(":8080",nil)
     }

让客户端重定向

处理器端代码

```go func handler(w http.ResponseWriter,r *http.Request){

代码语言:javascript
复制
  //以下操作必须得在WriteHeader之前运行
  w.Header().Set("Location","https://www.baidu.com") //第一个参数是表明Location地址,第二个参数指定重定向位置
  w.WriteHeader(302) //设置响应的状态码

}

代码语言:javascript
复制
3.   可以看出,*http.Request 参数是用来处理用户的请求

4.   http.ReponseWriter用来给用户响应

## 模板引擎,处理响应数据

1.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221104090340.png)

2.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20221104090544.png)

3.   ```go
     func testTemplate(w http.ResponseWriter,r *http.Request) {
     	//解析模板
     	t,_ := template.ParseFiles("index.html")
     	t.Execute(w,"")
     }

处理静态文件

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-10-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 搭建服务器
  • HTTP报文格式
    • adduser添加用户的方法
      • Exec执行一次命令,(包括查询,删除,更新,插入等),不返回任何执行结果,参数args表示query中的占位参数
        • Query执行一次查询,返回多行结果,(即Rows),一般用于执行select命令,参数args表示query中的占位参数
          • QueryRow执行一次查询,并期望返回最多一行结果(即Row),QueryRow总是返回非nil的值,查到返回值的Scan方法被调用时,才会返回被延迟的错误
            • 总:AddUser 添加User的方法一,需要预编译
              • 单元测试
                • 处理一条语句
                  • 处理多条语句
                    • 处理请求
                      • 切片上存储指针或带指针的值
                        • 获取请求行中的信息(url和传递的参数)
                          • refer属性
                          • 获取请求体中的信息
                        • 获取请求参数
                          • 通过直接调用FormValue方法和PostFormValue方法直接获取请求参数的值
                        • 处理客户端响应
                          • 让客户端重定向
                        • 处理静态文件
                        相关产品与服务
                        腾讯云服务器利旧
                        云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档