(11-3)网络操作:包net/http

11.3  包net/http

在Go语言中,包net/http提供了创建 Web 服务器和客户端的功能,支持 HTTP 和 HTTPS 协议。

11.3.1  包net/http的功能和内置成员

在Go语言中,包net/http的主要功能有:创建 HTTP 服务器和客户端、处理 HTTP 请求和响应、操作 HTTP 头信息、实现 HTTP 接口和HTTPS操作。以下是包net/http中的主要内置成员:

  1. 常量http.MethodGet、http.MethodPost、http.MethodPut、http.MethodDelete:表示 HTTP 请求的方法。
  2. 常量http.StatusContinue、http.StatusOK、http.StatusNotFound、http.StatusInternalServerError:表示 HTTP 响应状态码。
  3. 类型http.DefaultClient:表示一个默认的 HTTP 客户端,可以直接使用该客户端发送请求。
  4. 类型http.HandlerFunc:表示一个 HTTP 处理函数,可以将普通函数转换为 HTTP 处理器。
  5. 方法http.FileServer():创建一个静态文件服务器,并返回一个实现了 http.Handler 接口的对象,用于处理静态资源的请求。
  6. 类型http.Cookie:表示一个 HTTP Cookie,包括 Cookie 名称、值、过期时间等属性。

总之,包net/http是 Go 语言中用于实现 HTTP 协议的标准库,提供了丰富的功能和内置成员,可以方便地创建和处理 HTTP 请求、响应以及头信息等,同时也支持 HTTPS,是开发基于 HTTP 协议的 Web 应用程序的必要工具。

11.3.2  类型http.DefaultClient

http.DefaultClient是Go语言包net/http中的一个默认 HTTP 客户端实例,它提供了发送 HTTP 请求的方法,并支持连接池等高级特性。具体来说,http.DefaultClient 的主要功能如下:

  1. 发送 HTTP 请求:http.DefaultClient.Do() 方法可以发送一个 HTTP 请求,并返回一个 http.Response 对象。
  2. 支持超时和重试:http.Client.Timeout 属性表示客户端发送请求的超时时间;httpretry 包可以实现 HTTP 请求的重试逻辑。
  3. 支持连接池:http.Transport 类型是 http.DefaultClient 内部使用的 HTTP 连接池,可以复用 TCP 连接以提高效率,同时也支持最大空闲连接数、最大空闲时间等配置。
  4. 支持代理服务器:http.ProxyFromEnvironment() 方法可以根据环境变量自动设置代理服务器。

类型http.DefaultClient中的常用内置成员如下:

  1. http.Client.Timeout:表示客户端发送请求的超时时间;默认为无穷大。
  2. http.Client.Jar:表示客户端的 Cookie 存储器,用于管理 HTTP 请求和响应中的 Cookie。
  3. http.Client.CheckRedirect 属性:表示是否允许 HTTP 重定向,默认允许重定向。
  4. http.Client.Transport:表示客户端内部使用的 HTTP 连接池。

本实例实现了一个简单的命令行天气查询工具,用户可以通过命令行输入城市名称,程序会向 OpenWeatherMap 发送 HTTP 请求并获取该城市的天气预报信息。在开发本程序之前,需要先在OpenWeatherMap获取一个密钥,注册申请“API密钥”的地址是:

https://home.openweathermap.org/users/sign_up

实例11-5:天气预报程序(源码路径:Go-codes\11\tianqi.go

实例文件tianqi.go的具体实现代码如下所示。

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"strings"
)

const APIKey = "your_api_key_here" // 请替换为你的 OpenWeatherMap API Key

func main() {
	if len(os.Args) < 2 {
		fmt.Println("Usage: weather <city>")
		return
	}

	city := strings.Join(os.Args[1:], " ")

	url := fmt.Sprintf("https://api.openweathermap.org/data/2.5/weather?q=%s&units=metric&appid=%s", city, APIKey)

	resp, err := http.Get(url)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()

	var data map[string]interface{}

	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		fmt.Println(err)
		return
	}

	if data["cod"].(float64) == 200 {
		name := data["name"].(string)
		temp := data["main"].(map[string]interface{})["temp"].(float64)
		desc := data["weather"].([]interface{})[0].(map[string]interface{})["description"].(string)

		fmt.Printf("%s: %.1f℃ %s\n", name, temp, desc)
	} else {
		fmt.Println(data["message"])
	}
}

对上述代码的具体说明如下:

  1. 首先使用方法http.DefaultClient.Get()发送一个 HTTP GET 请求,并在 URL 中指定了城市名称和 OpenWeatherMap 的 API Key。
  2. 然后,使用方法json.NewDecoder()解析HTTP 响应,并提取出其中的城市名称、气温和天气描述等信息。
  3. 最后,将这些信息输出到命令行中。

执行后如果输入的城市名称是有效的,程序会输出该城市的名称、气温和天气描述等信息,例如:

go run weather.go Beijing
Beijing: 27.2℃ scattered clouds

如果输入的城市名称无效或发生其他错误,程序会输出相应的错误信息,例如:

go run weather.go invalid_city_name
city not found

11.3.3  类型http.HandlerFunc

http.HandlerFunc是Go语言包net/http中的一个类型,能够将普通函数转换成 http.Handler 接口。通过使用 http.HandlerFunc,可以方便地定义 HTTP 请求处理器,并且可以像普通函数一样传递参数和返回值。以下是 http.HandlerFunc 的主要功能:

  1. 实现 HTTP 处理器:http.HandlerFunc 实现了 http.Handler 接口,可以作为 HTTP 处理器使用,当有请求到达时会自动调用其 ServeHTTP() 方法。
  2. 支持传参和返回值:http.HandlerFunc 能够接收任意类型的函数,并将其转换为 http.Handler 接口,从而支持传参和返回值。

在类型http.HandlerFunc中主要包含如下所示的内置成员:

  1. http.HandlerFunc.ServeHTTP():表示 HTTP 请求处理器的核心方法,它接收一个 http.ResponseWriter 对象和一个 *http.Request 对象,并根据请求信息生成响应结果。
  2. http.HandlerFunc(func(w http.ResponseWriter, r *http.Request)) :表示用于创建 http.HandlerFunc 对象的构造函数。

请看下面的实例,功能是使用 http.HandlerFunc实现了一个简单的猜数字游戏。用户可以在浏览器中访问该服务,并通过输入框输入一个数字,程序会判断该数字是否等于随机生成的一个数字,然后给出相应的提示。

实例11-6:猜数字游戏(源码路径:Go-codes\11\guss.go

实例文件guss.go的具体实现代码如下所示。

import (
	"crypto/rand"
	"fmt"
	"math/big"
	"net/http"
	"strconv"
	"strings"
)

func main() {
	http.HandleFunc("/", guessHandler)

	fmt.Println("Guess game is running on http://localhost:8089")
	err := http.ListenAndServe(":8089", nil)
	if err != nil {
		fmt.Println(err)
	}
}

func guessHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		w.Write([]byte(`
			<html>
			<head><title>Guess Game</title></head>
			<body>
			<h1>Welcome to Guess Game!</h1>
			<p>I'm thinking of a number between 1 and 100. Can you guess it?</p>
			<form method="POST">
				<input type="text" name="guess" placeholder="Enter your guess">
				<input type="submit" value="Submit">
			</form>
			</body>
			</html>
		`))
	} else if r.Method == "POST" {
		guessStr := strings.TrimSpace(r.FormValue("guess"))
		guess, err := strconv.Atoi(guessStr)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("Invalid input"))
			return
		}

		target, err := rand.Int(rand.Reader, big.NewInt(100))
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			w.Write([]byte("Internal server error"))
			return
		}

		if guess == int(target.Int64())+1 {
			w.Write([]byte("Congratulations! You got it!"))
		} else if guess < int(target.Int64())+1 {
			w.Write([]byte("Too low! Try again."))
		} else {
			w.Write([]byte("Too high! Try again."))
		}
	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
		w.Write([]byte("Method not allowed"))
	}
}

对上述代码的具体说明如下:

  1. 首先定义了函数guessHandler(),并将其转换为 http.HandlerFunc 类型;
  2. 使用包crypto/rand中提供的方法Int()生成了一个大数(在本例中为 100),然后将其转换为一个整型数作为目标数字。这种方法比简单地使用随机种子更加安全和可靠。
  3. 当浏览器发送 GET 请求时,该函数会返回一个包含输入框的 HTML 页面;
  4. 当浏览器发送 POST 请求时,该函数会读取用户输入的数字并与随机生成的数字进行比较,然后给出相应的提示。

执行成功后在浏览器中访问http://localhost:8089,当在输入框输入猜测的数字时,程序会判断该数字是否等于随机生成的一个数字,然后给出相应的提示。如果你输入的数字与程序随机生成的数字相等,则会输出以下消息:

Congratulations! You got it!

如果你输入的数字小于程序随机生成的数字,则会输出以下消息:

Too low! Try again.

如果你输入的数字大于程序随机生成的数字,则会输出以下消息:

Too high! Try again.

如果你输入的不是数字,则会输出以下消息:

Invalid input

如果程序在内部遇到了错误,则会输出以下消息:

Internal server error

如果你使用了除 GET 和 POST 以外的 HTTP 方法,则会输出以下消息:

Method not allowed

最后,程序会一直运行在 http://localhost:8080 地址上,直到你按下 Ctrl+C 停止它。

11.3.4  类型http.Cookie

http.Cookie是Go语言包net/http中的一个类型,表示一个 HTTP Cookie。Cookie 是一种用于在客户端和服务器之间传递信息的机制,通常用于存储用户会话信息、记录用户偏好设置等。在类型http.Cookie中主要包含如下所示的内置成员:

  1. http.Cookie.Name:表示 Cookie 的名称。
  2. http.Cookie.Value:表示 Cookie 的值。
  3. http.Cookie.Path:表示 Cookie 的作用路径,默认值为 "/"。
  4. http.Cookie.Domain:表示 Cookie 的作用域名,默认值为当前访问的域名。
  5. http.Cookie.Expires:表示 Cookie 的过期时间。
  6. http.Cookie.MaxAge:表示 Cookie 的最大存活时间,以秒为单位。
  7. http.Cookie.Secure:表示 Cookie 只能通过 HTTPS 连接传输。
  8. http.Cookie.HttpOnly:表示 Cookie 不能被 JavaScript 访问。

下面是一个使用 http.Cookie的例子,实现了一个简单的登录功能。用户可以在浏览器中输入用户名和密码,并选择是否记住登录状态。如果勾选了“记住我”选项,程序会将用户登录信息保存到 Cookie 中,以便在下次访问时自动登录。

实例11-7:用户登录验证系统(源码路径:Go-codes\11\login.go

实例文件login.go的具体实现代码如下所示。

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

func indexHandler(w http.ResponseWriter, r *http.Request) {
	cookie, err := r.Cookie("username")

	// 如果存在 Cookie,则直接登录
	if err == nil && cookie.Value != "" {
		http.Redirect(w, r, "/welcome", http.StatusFound)
		return
	}

	// 否则显示登录表单
	tmpl := template.Must(template.ParseFiles("index.html"))
	tmpl.Execute(w, nil)
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	username := r.PostFormValue("username")
	password := r.PostFormValue("password")

	if username == "admin" && password == "admin123" {
		http.SetCookie(w, &http.Cookie{
			Name:  "username",
			Value: username,
		})
		http.Redirect(w, r, "/welcome", http.StatusFound)
	} else {
		tmpl := template.Must(template.ParseFiles("login.html"))
		err := tmpl.Execute(w, "Invalid username or password")
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	}
}

func welcomeHandler(w http.ResponseWriter, r *http.Request) {
	cookie, err := r.Cookie("username")
	if err != nil {
		http.Redirect(w, r, "/", http.StatusFound)
		return
	}

	tmpl := template.Must(template.ParseFiles("welcome.html"))
	err = tmpl.Execute(w, []string{cookie.Value})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}


func main() {
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/login", loginHandler)
	http.HandleFunc("/welcome", welcomeHandler)

	fmt.Println("Server starting...")
	http.ListenAndServe(":8089", nil)
}

在上述实例代码中定义了三个 HTTP 请求处理器函数:indexHandler()、loginHandler() 和 welcomeHandler(),具体说明如下:

  1. indexHandler():显示包含用户名和密码输入框的登录表单;
  2. loginHandler():处理用户提交的表单,判断用户是否输入正确的用户名和密码,并根据用户是否勾选“记住我”选项设置 Cookie;
  3. welcomeHandler():显示欢迎页面,如果存在有效的 Cookie则显示 Cookie中存储的用户名。

执行后,当我们访问 http://localhost:8089/login 时,会直接显示 login.html文件的登录表单内容,如图11-1所示。在该页面上可以输入用户名和密码,并选择是否记住登录状态。如果勾选了“记住我”选项,则下次访问时将自动登录。如果输入的用户名和密码正确,则会跳转到欢迎页面,并输出欢迎消息,如图11-2所示。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码农三叔

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

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

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

打赏作者

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

抵扣说明:

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

余额充值