Golang HTTP编程


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

1. 服务器配置

package main

import (
    "fmt"
    "net/http"
)

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

func test(res http.ResponseWriter, req *http.Request) {
    fmt.Println("hello") //提交给服务器
    fmt.Fprintln(res, "<h1>welcome testweb</h1>")
}
func main() {
    //路由到指定位置,跳转相关函数处理
    http.HandleFunc("/", hello)
    http.HandleFunc("/test", test)
    err := http.ListenAndServe("127.0.0.1:8091", 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>

在这里插入图片描述

页面展示

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
    }
}

在这里插入图片描述

输出到文本中

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
	}
}

在这里插入图片描述

逻辑模板

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

<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>

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

头发莫的了呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值