1. 源代码
package main
import (
"fmt"
"log"
"net/http"
"strings"
)
func sayhelloName(w http.ResponseWriter, r *http.Request) {
r.ParseForm() //解析参数,默认是不会解析的
fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println(r.Form["url_long"])
for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("val:", strings.Join(v, ""))
}
fmt.Fprintf(w, "Hello World!") //这个写入到w的是输出到客户端的
}
func main() {
http.HandleFunc("/", sayhelloName) //设置访问的路由
err := http.ListenAndServe(":9090", nil) //设置监听的端口
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
2. 代码解析
2.1 http.HandleFunc
在 Go 语言的 net/http 包中,HandleFunc 是一个用于注册处理 HTTP 请求的函数。它将一个 URL 路径和一个处理该路径的函数关联起来。当 HTTP 服务器收到一个请求时,它将根据请求的 URL 路径找到对应的处理函数,并调用该函数处理请求。
HandleFunc 的原型如下:
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
其中,pattern 是一个字符串,表示 URL 路径的模式;handler 是一个函数,它接受两个参数:一个 ResponseWriter 接口和一个指向 Request 结构的指针。ResponseWriter 接口用于向客户端发送 HTTP 响应,Request 结构包含了客户端的 HTTP 请求信息。
在上面的例子中,我们打印了request 结构体的信息,同时将Hello World!
写入到response 当中,在服务终端的打印里面,答应结果如下。
map[]
path /
scheme
[]
map[]
path /favicon.ico
scheme
[]
在客户端浏览器当中的解析接过如图,我们在请求当中访问本地:127.0.0.1:9090路径即可。
2.2 http.Request
http.Request
是 Go 语言 net/http 包中的一个结构体,用于表示一个 HTTP 请求。它包含了客户端发送的请求信息,如 HTTP 方法、URL、头部信息、请求正文等。以下是 http.Request 结构体的定义:
type Request struct {
Method string
URL *url.URL
Proto string // "HTTP/1.0"
ProtoMajor int // 1
ProtoMinor int // 0
Header Header
Body io.ReadCloser
GetBody func() (io.ReadCloser, error)
ContentLength int64
TransferEncoding []string
Close bool
Host string
Form url.Values
PostForm url.Values
MultipartForm *multipart.Form
Trailer Header
RemoteAddr string
RequestURI string
TLS *tls.ConnectionState
Cancel <-chan struct{}
Response *Response
ctx context.Context
}
结构体当中的主要字段如下:
Method
: 请求的 HTTP 方法(如 “GET”、“POST” 等)。
URL
: 请求的 URL,为 *url.URL 类型。
Proto
: 请求使用的 HTTP 协议版本,如 “HTTP/1.1”。
ProtoMajor
和 ProtoMinor: 分别表示 HTTP 协议的主版本号和次版本号。
Header
: 请求的头部信息,为 http.Header 类型,存储键值对形式的头部信息。
Body
: 请求的正文,为 io.ReadCloser 类型。它是一个可读的数据流,可以用于读取请求的正文数据。
ContentLength
: 请求正文的长度,为 int64 类型。
TransferEncoding
: 请求的传输编码,为字符串切片类型。
Host
: 请求的主机名,包括域名和端口号。
Form
: 请求的表单数据,为 url.Values 类型。用于解析 “application/x-www-form-urlencoded” 类型的表单数据。
PostForm
: 请求的 POST 表单数据,为 url.Values 类型。用于解析 “application/x-www-form-urlencoded” 类型的 POST 表单数据。
MultipartForm
: 请求的多部分表单数据,为 *multipart.Form 类型。用于解析 “multipart/form-data” 类型的表单数据。
Trailer
: 请求的尾部头信息,为 http.Header 类型。用于解析 “trailer” 头部信息。
RemoteAddr
: 请求的远程地址,包括 IP 地址和端口号。
RequestURI
: 请求的原始 URL 路径。
TLS
: 请求的 TLS 连接状态,为 *tls.ConnectionState 类型。如果请求是 HTTPS 请求,则此字段包含 TLS 连接的详细信息。
ctx
: 请求的上下文,为 context.Context 类型。用于在处理请求时传递元数据和超时/取消信号。
通常情况下,你不需要直接创建 http.Request 结构体的实例,而是通过处理函数的参数接收到一个指向 http.Request 结构体的指针。你可以通过这个指针访问和操作请求的各种信息。
2.3 http.ResponseWriter
http.ResponseWriter
是 Go 语言 net/http 包中的一个接口,用于表示 HTTP 响应的写入器。在处理 HTTP 请求时,你可以使用 http.ResponseWriter 向客户端发送 HTTP 响应,包括状态码、头部信息和响应正文。以下是 http.ResponseWriter 接口的定义:
type ResponseWriter interface {
Header() Header
Write([]byte) (int, error)
WriteHeader(statusCode int)
}
http.ResponseWriter
接口包含三个方法,以下是这些方法的简要说明:
Header() Header
: 返回一个表示响应头部信息的 http.Header 类型。你可以通过这个方法获取到一个 http.Header 实例,并修改它以设置响应的头部信息。例如,设置响应的内容类型:
w.Header().Set("Content-Type", "application/json")
Write([]byte) (int, error)
: 将字节切片写入响应正文。这个方法接受一个字节切片作为参数,并将其写入响应正文。它返回写入的字节数和可能遇到的错误。例如,向客户端发送一个简单的响应:
response := []byte("Hello, world!")
w.Write(response)
WriteHeader(statusCode int):
设置响应的状态码。这个方法接受一个整数作为参数,并将其设置为响应的状态码。注意,你应该在写入响应正文之前调用此方法。例如,设置一个 404 Not Found 响应:
w.WriteHeader(http.StatusNotFound)
通常情况下,你不需要自己实现 http.ResponseWriter 接口。当你编写一个处理 HTTP 请求的函数时,你会接收到一个指向 http.ResponseWriter 接口的指针。你可以通过这个指针调用上述方法来发送 HTTP 响应。例如:
func myHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
w.Write([]byte("Hello, world!"))
}
2.4 http.ListenAndServe
http.ListenAndServe
是 Go 语言 net/http 包中的一个函数,用于启动一个 HTTP 服务器并监听指定的地址和端口。它会接收客户端的 HTTP 请求,根据请求的 URL 路径找到对应的处理函数,并调用该函数处理请求。以下是 http.ListenAndServe 函数的原型:
func ListenAndServe(addr string, handler Handler) error
http.ListenAndServe
接受两个参数:
addr (string)
: 要监听的地址和端口,格式为 “host:port”。例如,“:8080” 表示监听所有可用的网络接口上的 8080 端口。
handler (Handler)
: 用于处理 HTTP 请求的处理器。Handler 是一个接口,你可以实现该接口来处理请求。通常情况下,你可以使用 http.DefaultServeMux(默认的多路复用器)处理请求,它会根据请求的 URL 路径调用已注册的处理函数。要使用 http.DefaultServeMux,只需将 handler 参数设置为 nil 即可。
http.ListenAndServe
函数返回一个错误,如果启动 HTTP 服务器失败,它会返回一个描述错误的 error 类型。
3.基础知识
3.1 HTTP 协议
HTTP(HyperText Transfer Protocol,超文本传输协议)是一种用于传输超文本(如 HTML、CSS、JavaScript 等)的应用层协议。它是互联网上最广泛使用的协议,构成了万维网(World Wide Web)的基础。HTTP 协议基于请求-响应模型,客户端(如浏览器)通过发送 HTTP 请求来获取资源,服务器接收并处理请求后返回一个 HTTP 响应。
HTTP 协议的主要特点包括:
无状态:HTTP 协议本身不维护客户端和服务器之间的状态信息。这意味着每个请求都是独立的,服务器无法识别连续的请求之间的关系。为了实现状态管理,可以使用 Cookie 和 Session 等技术。
简单快速:HTTP 协议简单易懂,客户端和服务器之间的通信过程简洁高效。
可扩展:HTTP 协议允许自定义请求方法、头部字段和状态码,这使得它具有很好的扩展性。
灵活:HTTP 协议允许传输任何类型的数据,通过 Content-Type 头部字段指定数据类型。
HTTP 协议包括一系列请求方法,如 GET、POST、PUT、DELETE 等,用于指定请求的类型和操作。HTTP 请求和响应都包含头部信息(Header)和正文(Body)。头部信息用于描述请求或响应的元数据,如内容类型、编码方式、认证信息等。正文包含实际的数据,如 HTML 文档、JSON 数据等。
HTTP 协议有多个版本,其中最常用的是 HTTP/1.1 和 HTTP/2。HTTP/1.1 是目前最广泛使用的版本,它引入了持久连接、管道化请求等特性以提高性能。HTTP/2 是最新的版本,它采用了二进制帧、多路复用等技术,进一步提高了传输效率和性能。此外,还有 HTTPS 协议,它是在 HTTP 协议的基础上加入了 SSL/TLS 加密层,用于保护数据的安全性和隐私性。
3.2 POST 方法
HTTP 协议中的 POST 方法是一种用于提交数据到服务器的方法。POST 请求通常用于创建或更新资源,如提交表单、上传文件等。与 GET 方法相比,POST 方法具有以下特点:
非安全:POST 方法会对服务器上的资源产生影响,如创建新资源或修改现有资源。因此,它不是安全的。
非幂等:多次执行相同的 POST 请求可能导致不同的结果。例如,多次提交相同的表单可能导致多个资源被创建。
不可缓存:根据 HTTP 规范,POST 请求的响应不应被客户端(如浏览器)缓存。
请求参数:POST 请求的参数通常通过请求正文(Body)传递,而不是 URL 的查询字符串。这使得 POST 请求能够发送更大的数据量,并提供更好的隐私保护。
以下是一个简单的 POST 请求示例:
POST /submit HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 13
name=test&age=20
在这个示例中,客户端向服务器发送了一个 POST 请求,请求提交数据到 “/submit” 路径。请求正文包含了两个参数:name 和 age。服务器接收到请求后,会根据请求的路径和正文数据处理请求(例如,将数据存储到数据库),并返回一个响应给客户端。
3.3 GET 方法
HTTP 协议中的 GET 方法是一种用于请求和获取指定资源的方法。GET 请求通常用于从服务器检索信息,而不是修改或创建资源。GET 方法具有以下特点:
安全:GET 方法是安全的,因为它不会对服务器上的资源产生任何影响。换句话说,GET 请求仅用于读取数据,而不是修改数据。
幂等:多次执行相同的 GET 请求,其结果也应该相同。无论执行一次还是多次,服务器上的资源状态都不会发生变化。
可缓存:GET 请求的响应可以被客户端(如浏览器)缓存,以便将来重用。这可以提高性能,减少服务器的负载。
请求参数:GET 请求的参数通常通过 URL 的查询字符串传递。查询字符串是 URL 中 “?” 符号后面的部分,包含一系列键值对,表示请求参数。例如:http://example.com/search?q=keyword
以下是一个简单的 GET 请求示例:
GET /search?q=keyword HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
Accept: text/html
在这个示例中,客户端向服务器发送了一个 GET 请求,请求获取 “/search” 路径下的资源,并通过查询字符串传递了一个名为 “q” 的参数,值为 “keyword”。服务器接收到请求后,会根据请求的路径和参数找到对应的资源,并将其作为响应返回给客户端。