Go语言-HTTP编程

目录

前言

1、服务器配置

2、客户端配置

3、请求方法

4、panic宕机恢复

5、模板

6、MYSQL


前言

Go原生支持http,直接使用import("net/http")即可,http服务性能和nginx非常接近,都具备高并发支持的能力,代码实现起来比较简单。

1、服务器配置

package main

import (
	"fmt"
	"net/http"
)

//HTTP服务端配置
//业务请求相应处理
func hello(res http.ResponseWriter, req *http.Request) {
	fmt.Println("hello")
	fmt.Fprintln(res, "<h1>welcome</h1>")
}
func main() {
	//路由到指定站点位置跳转相关函数处理
	http.HandleFunc("/", hello)
	err := http.ListenAndServe("127.0.0.1:8088", nil)
	if err != nil {
		fmt.Println("启动监听失败,err:", err)
		return
	}
}

启动服务端

这时候用浏览器验证

2、客户端配置

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

//http客户端配置

func main() {
    /*
        http.Get()
        传入值为 url,是string类型
        返回值有两个,
        一个是请求,结构体类型,该结构体的Body字段是一个接口
        第二个是err
    */
    //在Get函数中,放入完整合格域名(FQDN)
    res, err := http.Get("https://www.baidu.com/")
    if err != nil {
        fmt.Println("获取信息失败,err:", err)
        return
    }
    /*
        接收 io.reader,这是一个接口
        返回字节切片和 err
    */
    //response结构体中读取信息
    buf, err := ioutil.ReadAll(res.Body)
    if err != nil {
        fmt.Println("读取信息失败,err:", err)
        return
    }
    fmt.Println(string(buf))
}

运行客户端

3、请求方法

Get请求:提交的数据都是追加在URL之后,限制点是URL的长度不能超过8K,不适用于提交量大的数据,适用于请求数据,get请求从服务器中读取资源,以明文形式发送请求。

Post请求:数据存放在包中,数据量不受限制,可以提交上传量大的数据信息,post用于更新服务器资源。

Put请求:用于在服务器上创建资源。

Delete请求:用户在服务器上删除资源。

Head请求:向服务器请求头部信息,用于监控服务状态。

示例:

package main

import (
	"fmt"
	"net/http"
)

//请求头部信息

var url []string = []string{
	"https://www.baidu.com/",
	"https://www.taobao.com/",
	"https://www.google.com/",
}

func main() {
	for _, v := range url {
		req, err := http.Head(v)
		if err != nil {
			fmt.Println("获取请求失败,err", err)
			return
		}
		fmt.Printf("来自%s的网站,状态是%s\n", v, req.Status)
	}
}

运行结果为

使用自建客户端进行超时时间优化

package main

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

//请求头部信息

var url []string = []string{
	"https://www.baidu.com/",
	"https://www.taobao.com/",
	"https://www.google.com/",
}

func main() {
	for _, v := range url {
		//自建client做测试,优化超时时间
		client := http.Client{
			Transport: &http.Transport{
				//连接服务端
				Dial: func(network, addr string) (net.Conn, error) {
					//设置超时时间2秒
					timeout := time.Second * 2
					return net.DialTimeout(network, addr, timeout)
				},
			},
		}
		//使用自建客户端测试
		req, err := client.Head(v)
		if err != nil {
			fmt.Println("获取请求失败,err", err)
			return
		}
		fmt.Printf("来自%s的网站,状态是%s\n", v, req.Status)
	}
}

再次运行结果为

常见的状态码

http.StatusContinue =100客户端上传数据需要请求服务端同意才可以上传显示的状态码。
http.StatusOK= 200成功连接访问。
http.StatusFound = 302页面跳转的状态码。
http.StatusBadRequest = 400非法请求,服务端无法解析。
http.StatusUnauthorized = 401 权限受限,未通过。
http.StatusForbidden = 403禁止访问。
http.StatusNotFound = 404请求页面不存在。
http.StatusInternalServerError = 500服务器内部错误。

示例:表单业务。

package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

//定义页面表单html
const form = `<html><head></head><body>
	<form action="#" method="post" name="bar">
		<input type="test" name="in"/>
		<input type="test" name="in"/>
		<input type="submit" value="submit"/>
	</form></body></html>`

//业务请求相应处理
func hello(res http.ResponseWriter, req *http.Request) {
	fmt.Println("hello") //提交给服务器
	fmt.Fprintln(res, "<h1>welcome China</h1>")
}

//form表单处理
func formServer(res http.ResponseWriter, req *http.Request) {
	//头部申明
	res.Header().Set("Content-Type", "text/html")
	//类型判断
	switch req.Method {
	case "GET":
		io.WriteString(res, form)
	case "POST":
		req.ParseForm()
		//读取第一个框体内容给respomse做页面回应
		io.WriteString(os.Stdout, req.Form["in"][0])
		io.WriteString(res, "\n")
		io.WriteString(res, req.FormValue("in"))
	}
}
func main() {
	//路由到指定位置,跳转相关函数处理
	http.HandleFunc("/test1", hello)
	http.HandleFunc("/test2", formServer)
	if err := http.ListenAndServe("127.0.0.1:8088", nil); err != nil {
		fmt.Println("启动监听失败,err:", err)
		return
	}
}

启动服务

再用浏览器查看效果

4、panic宕机恢复

web 服务为了防止 goroutine 运行 panic 而终止程序,提出了宕机恢复解决方案。

Recover是一个 Go 语言的内建函数,可以让进入宕机流程中的 goroutine 恢复过来,recover 仅在延迟函数 defer中有效,在正常的执行过程中,调用 recover 会返回nil并且没有其他任何效果,如果当前的 goroutine 陷入恐慌,调用 recover 可以捕获到 panic 的输入值,并且恢复正常的执行。

通常来说,不应该对进入 panic 宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,举个例子,当web服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端─直处于等待状态,如果web服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。

在其他语言里,宕机往往以异常的形式存在,底层抛出异常,上层逻辑通过try/catch机制捕获异常,没有被捕获的严重异常会导致宕机,捕获的异常可以被忽略,让代码继续运行。

Go语言没有异常系统,其使用 panic 触发宕机类似于其他语言的抛出异常,recover的宕机恢复机制就对应其他语言中的try/catch机制。

panic和recover的组合有如下特性:
有 panic 没 recover,程序宕机;
有 panic 也有 recOver,程序不会宕机,执行完对应的 defer 后,从宕机点退出当前函数后继续执行。

//异常捕捉
package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
)

//页面表单
const form = `<html><head><title>web</title></head><body>
 <form action="#" method="post" name="bar">
  <input type="text" name="in"/>
  <input type="text" name="in"/>
  <input type="submit" name="提交">
 </form></body></html>`

//HTTP服务端配置

//test1业务请求响应处理
func hello(res http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(res, "<h1>welcome</h1>") //客户端访问浏览器显示
	panic("test1 panic")
}

//form表单处理,使用ResponseWriter在浏览器输出
func formServer(res http.ResponseWriter, req *http.Request) {
	//头部声明,表明传的是html的信息
	res.Header().Set("Content-Type", "text/html")
	//提交类型判断
	switch req.Method {
	case "GET":
		io.WriteString(res, form)
	case "POST":
		req.ParseForm()

		/*
		   WriteString第一个参数是一个接口,所以不仅可以输出到浏览器,还可以将读取的信息输出到任意地方
		   也可以将内容输出到控制台 os.stdout,也可以写入到文件等
		*/
		//读取第一个框体内容给response 做页面回应.连个框体名字都是in,
		//如果读取第二个,则把下标替换为1
		io.WriteString(res, req.Form["in"][0])
		//两种方式输出,第一个框体中的信息提交后将会输出两次,第二个框体没有输出
		io.WriteString(res, "\n")
		io.WriteString(res, req.FormValue("in"))
	}
}

func main() {

	//将要捕获的函数,放入到异常捕捉函数里
	http.HandleFunc("/test1", logPanic(hello))

	http.HandleFunc("/test2", logPanic(formServer))

	err := http.ListenAndServe("127.0.0.1:8088", nil)
	if err != nil {
		fmt.Println("启动监听失败,err:", err)
		return
	}
}

//宕机恢复recovere,注意,recover 使用在defer中
//将函数http.HandlerFunc作为值传入,返回值也是
func logPanic(handle http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		//宕机恢复,这里使用匿名函数,recover 必须在defer 里使用
		defer func() {
			if x := recover(); x != nil {
				log.Printf("%v,捕捉异常:%v", r.RemoteAddr, x)
			}
		}()
		handle(w, r)
	}
}

启动服务

再用浏览器验证效果

5、模板

模板是用于动态生成页面或者用于代码生成器的编写。

示例:

main.go

package main

import (
	"fmt"
	"os"
	"text/template"
)

type Persion struct {
	Name  string
	Age   int
	Title string
}

//映射key:string value不限制类型
// var arr map[string]interface{}

//template模板引用
func main() {
	t, err := template.ParseFiles("index.html")
	if err != nil {
		fmt.Println("模板读取失败,err:", err)
		return
	}
	//实例Persion
	persion := Persion{Name: "tom", Age: 20, Title: "个人网站"}
	if err := t.Execute(os.Stdout, persion); err != nil {
		fmt.Println("输出错误,err:", err.Error())
	}
}

index.html

<html>
    <head>
        <title>{{.Title}}</title>
    </head>
    <body>
        <h2>{{.Name}}</h2>
        <h2>{{.Age}}</h2>
    </body>
</html>

运行结果为

示例:页面展示。

main.go

package main

import (
    "fmt"
    "net/http"
    "text/template"
)

var myTemplate *template.Template

func initTemplate(filename string) (err error) {
    myTemplate, err = template.ParseFiles(filename)
    if err != nil {
        fmt.Println("模板加载失败,err", err)
        return
    }
    return
}

//业务请求相应处理
func userinfo(res http.ResponseWriter, req *http.Request) {
    //参数结构体
    type Person struct {
        Name  string
        Age   int
        Title string
    }
    p := Person{Name: "json", Age: 20, Title: "智慧城市"}
    myTemplate.Execute(res, p)
}

func main() {
    //路由到指定位置,跳转相关函数处理
    initTemplate("index.html")
    http.HandleFunc("/user/info", userinfo)
    //  http.HandleFunc("/test2", formSever)
    err := http.ListenAndServe("127.0.0.1:8091", nil)
    if err != nil {
        fmt.Println("启动监听失败,err:", err)
        return
    }
}

index.html

<html>
    <head>
        <title>{{.Title}}</title>
    </head>
    <body>
        <h1>Person Template</h1>
        <h2>{{.Name}}</h2>
        <h2>{{.Age}}</h2>
    </body>
</html>

启动服务

浏览器验证

输出到文本中

main.go

package main

import (
	"fmt"
	"net/http"
	"os"
	"text/template"
)

var myTemplate *template.Template

func initTemplate(filename string) (err error) {
	myTemplate, err = template.ParseFiles(filename)
	if err != nil {
		fmt.Println("模板加载失败,err", err)
		return
	}
	return
}

//业务请求相应处理
func userinfo(res http.ResponseWriter, req *http.Request) {
	//参数结构体
	type Person struct {
		Name  string
		Age   int
		Title string
	}
	p := Person{Name: "json", Age: 20, Title: "智慧城市"}
	//写入文件
	file, err := os.OpenFile("log.txt", os.O_CREATE|os.O_WRONLY, 0755)
	if err != nil {
		fmt.Println("文件加载失败,err", err)
		return
	}
	myTemplate.Execute(file, p)
	//写入浏览器
	myTemplate.Execute(res, p)
}

func main() {
	//路由到指定位置,跳转相关函数处理
	initTemplate("index.html")
	http.HandleFunc("/user/info", userinfo)
	//  http.HandleFunc("/test2", formSever)
	err := http.ListenAndServe("127.0.0.1:8091", nil)
	if err != nil {
		fmt.Println("启动监听失败,err:", err)
		return
	}
}

运行服务

逻辑模板

main.go

package main

import (
	"fmt"
	"net/http"
	"os"
	"text/template"
)

var myTemplate *template.Template

func initTemplate(filename string) (err error) {
	myTemplate, err = template.ParseFiles(filename)
	if err != nil {
		fmt.Println("模板加载失败,err", err)
		return
	}
	return
}

//业务请求相应处理
func userinfo(res http.ResponseWriter, req *http.Request) {
	//参数结构体
	type Person struct {
		Name  string
		Age   int
		Title string
	}
	p := Person{Name: "json", Age: 40, Title: "个人网站"}
	//写入文件
	file, err := os.OpenFile("log.txt", os.O_CREATE|os.O_WRONLY, 0755)
	if err != nil {
		fmt.Println("文件加载失败,err", err)
		return
	}
	myTemplate.Execute(file, p)
	//写入浏览器
	myTemplate.Execute(res, p)
}

func main() {
	//路由到指定位置,跳转相关函数处理
	initTemplate("index.html")
	http.HandleFunc("/user/info", userinfo)
	//  http.HandleFunc("/test2", formSever)
	err := http.ListenAndServe("127.0.0.1:8091", nil)
	if err != nil {
		fmt.Println("启动监听失败,err:", err)
		return
	}
}

index.html

在模板里可以做判断。设置超过30,显示this is old man:不超过30,会显示this is young man。

<html>
    <head>
        <title>{{.Title}}</title>
    </head>
    <body>
        {<h1>Persion Template</h1>
        {{if gt .Age 30}}
        <h2>this is old man,姓名:{{.Name}},年龄:{{.Age}}</h2>
        {{else}}
        <h2>this is young man,姓名:{{.Name}},年龄:{{.Age}}</h2>
        {{end}}
    </body>
</html>

启动服务

浏览器验证

6、MYSQL

在本地安装mysql5.7

安装完成后设置环境变量

配置 Navicat

终端测试

创建库

创建表

安装 Git

在这里插入图片描述

设置环境变量

在Go工作目录下安装go的mysql连接驱动

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

验证插件是否安装好

如果安装失败把下面的参数进行修改

示例:添加数据。

package main

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

type Person struct {
	UserId   int    `db:"user_id"`
	UserName string `db:"username"`
	Sex      string `db:"sex"`
	Email    string `db:"email"`
}

//数据库结构体
var DB *sqlx.DB

func init() {
	//连接数据库,"用户名:密码@协议(地址:端口)/数据库名"
	database, err := sqlx.Open("mysql", "root:abc123@tcp(127.0.0.1:3306)/school")
	if err != nil {
		fmt.Println("打开数据库失败,err:", err)
		return
	}
	DB = database
}

func main() {
	//执行数据库数据插入
	result, err := DB.Exec("insert into person (username,sex,email) values (?,?,?)", "tom", "boy", "tom@qq.com")
	if err != nil {
		fmt.Println("数据写入失败,err:", err)
		return
	}
	num, err := result.LastInsertId()
	if err != nil {
		fmt.Println("结果获取失败,err:", err)
		return
	}
	fmt.Println("data insert succ", num)
	if num != 0 {
		fmt.Println("数据写入成功")
	}
}

运行结果为:

回到数据库界面展示

示例:查询信息。

package main

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

type Person struct {
	UserId   int    `db:"user_id"`
	UserName string `db:"username"`
	Sex      string `db:"sex"`
	Email    string `db:"email"`
}

//数据库结构体
var DB *sqlx.DB

func init() {
	//连接数据库,"用户名:密码@协议(地址:端口)/数据库名"
	database, err := sqlx.Open("mysql", "root:abc123@tcp(127.0.0.1:3306)/school")
	if err != nil {
		fmt.Println("打开数据库失败,err:", err)
		return
	}
	DB = database
}

func main() {
	//定义获取数据的切片,用来装载查询结果
	var person []Person
	err := DB.Select(&person, "select user_id,username,sex,email from person where user_id=?", 1)
	if err != nil {
		fmt.Println("查询数据失败,err:", err)
		return
	}
	fmt.Println(person)
}

在终端查询结果

示例:在web页面展示数据。

package main

import (
	"fmt"
	"io"
	"net/http"
	"strconv"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

//定义页面表单HTML
const form = `<html><body><form action="#" method="post" name="bar">
                    <input type="text" name="in"/>
                    <input type="submit" value="Submit"/>
             </form></body></html>`

type Person struct {
	UserId   int    `db:"user_id"`
	UserName string `db:"username"`
	Sex      string `db:"sex"`
	Email    string `db:"email"`
}

//数据库结构体
var DB *sqlx.DB

func init() {
	//连接数据库,"用户名:密码@协议(地址:端口)/数据库名"
	database, err := sqlx.Open("mysql", "root:abc123@tcp(127.0.0.1:3306)/school")
	if err != nil {
		fmt.Println("打开数据库失败,err:", err)
		return
	}
	DB = database
}

//form表单处理
func formServer(res http.ResponseWriter, req *http.Request) {
	//头部申明
	res.Header().Set("Content-Type", "text/html")
	//提交类型判断
	switch req.Method {
	case "GET":
		io.WriteString(res, form)
	case "POST":
		req.ParseForm()
		//获取from框体中的数据,转换成int类型
		id, err := strconv.Atoi(req.Form["in"][0])
		if err != nil {
			fmt.Println("数据类型转换失败,err:", err)
			return
		}
		//定义获取数据的切片,用来装载查询结果
		var person []Person
		err = DB.Select(&person, "select user_id,username,sex,email from person where user_id=?", id)
		if err != nil {
			fmt.Println("查询数据失败,err:", err)
			return
		}
		//获取查询结果信息
		for _, v := range person {
			io.WriteString(res, strconv.Itoa(v.UserId))
			io.WriteString(res, "\n")
			io.WriteString(res, v.UserName)
			io.WriteString(res, "\n")
			io.WriteString(res, v.Sex)
			io.WriteString(res, "\n")
			io.WriteString(res, v.Email)
			io.WriteString(res, "\n")
		}
	}
}

func main() {
	http.HandleFunc("/mysql", formServer)
	if err := http.ListenAndServe("127.0.0.1:8088", nil); err != nil {
		fmt.Println("启动监听失败,err:", err)
		return
	}
}

启动服务后,在浏览器查看

示例:更新数据。

package main

import (
	"fmt"
	"io"
	"net/http"
	"strconv"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

//定义页面表单HTML
const form = `<html><body><form action="#" method="post" name="bar">
                    <p>ID<input type="text" name="in"/></p>
					<p>UserName<input type="text" name="in"/></p>
                    <input type="submit" value="Submit"/>
             </form></body></html>`

type Person struct {
	UserId   int    `db:"user_id"`
	UserName string `db:"username"`
	Sex      string `db:"sex"`
	Email    string `db:"email"`
}

//数据库结构体
var DB *sqlx.DB

func init() {
	//连接数据库,"用户名:密码@协议(地址:端口)/数据库名"
	database, err := sqlx.Open("mysql", "root:abc123@tcp(127.0.0.1:3306)/school")
	if err != nil {
		fmt.Println("打开数据库失败,err:", err)
		return
	}
	DB = database
}

//form表单处理
func formServer(res http.ResponseWriter, req *http.Request) {
	//头部申明
	res.Header().Set("Content-Type", "text/html")
	//提交类型判断
	switch req.Method {
	case "GET":
		io.WriteString(res, form)
	case "POST":
		req.ParseForm()
		//获取from框体中的数据,转换成int类型
		id, err := strconv.Atoi(req.Form["in"][0])
		if err != nil {
			fmt.Println("数据类型转换失败,err:", err)
			return
		}
		rename := req.Form["in"][1]
		//更新数据
		_, err = DB.Exec("update person set username=? where user_id=?", rename, id)
		if err != nil {
			fmt.Println("更新数据失败,err:", err)
			return
		} else {
			io.WriteString(res, "update succ")
		}

	}
}

func main() {
	http.HandleFunc("/mysql", formServer)
	if err := http.ListenAndServe("127.0.0.1:8088", nil); err != nil {
		fmt.Println("启动监听失败,err:", err)
		return
	}
}

启动服务后在浏览器修改信息

数据库查看结果

示例:删除数据。

package main

import (
	"fmt"
	"io"
	"net/http"
	"strconv"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

//定义页面表单HTML
const form = `<html><body><form action="#" method="post" name="bar">
                    ID<input type="text" name="in"/>
                    <input type="submit" value="Submit"/>
             </form></body></html>`

type Person struct {
	UserId   int    `db:"user_id"`
	UserName string `db:"username"`
	Sex      string `db:"sex"`
	Email    string `db:"email"`
}

//数据库结构体
var DB *sqlx.DB

func init() {
	//连接数据库,"用户名:密码@协议(地址:端口)/数据库名"
	database, err := sqlx.Open("mysql", "root:abc123@tcp(127.0.0.1:3306)/school")
	if err != nil {
		fmt.Println("打开数据库失败,err:", err)
		return
	}
	DB = database
}

//form表单处理
func formServer(res http.ResponseWriter, req *http.Request) {
	//头部申明
	res.Header().Set("Content-Type", "text/html")
	//提交类型判断
	switch req.Method {
	case "GET":
		io.WriteString(res, form)
	case "POST":
		req.ParseForm()
		//获取from框体中的数据,转换成int类型
		id, err := strconv.Atoi(req.Form["in"][0])
		if err != nil {
			fmt.Println("数据类型转换失败,err:", err)
			return
		}
		//删除数据
		_, err = DB.Exec("delete from person where user_id=?", id)
		if err != nil {
			fmt.Println("del data,err:", err)
			return
		} else {
			io.WriteString(res, "del succ")
		}

	}
}

func main() {
	http.HandleFunc("/mysql", formServer)
	if err := http.ListenAndServe("127.0.0.1:8088", nil); err != nil {
		fmt.Println("启动监听失败,err:", err)
		return
	}
}

启动服务后在web界面进行删除

数据库查看信息

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值