HTTP 编程(三):HTTP/HTTPS 请求处理
本篇教程我们将介绍 HTTP 服务端技术,包括如何处理 HTTP 请求和 HTTPS 请求。
处理 HTTP 请求
服务端实现
使用 net/http 包提供的 http.ListenAndServe()
方法,可以开启一个 HTTP 服务器,并且在指定的 IP 地址和端口上监听客户端请求,该方法的原型如下:
func ListenAndServe(addr string, handler Handler) error
该方法有两个参数:第一个参数 addr
表示监听的 IP 地址及端口号;第二个参数表示服务端对应的处理程序,通常为空,意味着将调用 http.DefaultServeMux
进行处理,而服务端编写的业务逻辑处理程序 http.Handle()
或 http.HandleFunc()
默认会被注入到 http.DefaultServeMux
中。
下面我们按照上述思路实现一个最基本的 HTTP 服务器 server.go
:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {
params := request.URL.Query();
fmt.Fprintf(writer, "你好, %s", params.Get("name"))
})
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatalf("启动 HTTP 服务器失败: %v", err)
}
}
在这段代码中,我们通过 http.HandleFunc
方法定义了一个 /hello
路由及对应处理程序,在这个处理程序中我们会返回一个欢迎字符串,其中还引用了客户端传递过来的请求参数。然后通过 http.ListenAndServe
方法启动 HTTP 服务器,监听本地 IP 的 8080
端口,当客户端请求 http://127.0.0.1:8080/hello
URL 时 HTTP 服务器会将其转发给默认的 http.DefaultServeMux
处理,这里最终会调用 http.HandleFunc
方法定义的处理器处理请求返回响应。
客户端请求
我们将前面教程实现的 HTTP 客户端示例代码 client.go
中的请求地址调整如下:
req, err := http.NewRequest("GET", "http://127.0.0.1:8080/hello?name=学院君", nil)
接下来,启动 HTTP 服务器:
go run server.go
然后新开一个 Terminal 窗口执行客户端调用代码:
打印出了预期结果,说明 HTTP 服务器可以正常提供服务。
处理 HTTPS 请求
服务端逻辑
net/http 包还提供 http.ListenAndServeTLS()
方法,用于处理 HTTPS 连接请求:
func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) error
ListenAndServeTLS()
和 ListenAndServe()
的行为一致,区别在于前者只处理 HTTPS 请求。要正确处理 HTTPS 请求,服务器上必须存在 SSL 证书和与之匹配的私钥相关文件,比如 certFile
对应 SSL 证书文件存放路径,keyFile
对应证书私钥文件路径。如果证书是由证书颁发机构签署的,certFile
参数指定的路径必须是存放在服务器上的经由 CA 认证过的 SSL 证书。
通过 OpenSSL 生成自签名证书
这里我们在本地演示 HTTPS 请求的处理,可以通过 OpenSSL 工具生成自签名证书:
# Generate CA private key
openssl genrsa -out ca.key 2048
# Generate CSR
openssl req -new -key ca.key -out ca.csr
# Generate Self Signed certificate(CA 根证书)
openssl x509 -req -days 365 -in ca.csr -signkey ca.key -out ca.crt
如果是通过 IP 地址访问 HTTPS 服务,红框内的部分请填写 IP 地址,比如 127.0.0.1
,如果通过域名访问,可以将其设置为域名:
这些文件都保存到 HTTPS 服务器代码所在目录:
服务端代码
开启 HTTPS 监听服务也很简单,除了调用方法调整为 http.ListenAndServeTLS
并传入上面生成的 SSL 证书和私钥文件外,其它都和 HTTP 服务器一样,我们在 https
目录下新建 server.go
编写对应代码:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {
params := request.URL.Query();
fmt.Fprintf(writer, "你好, %s", params.Get("name"))
})
err := http.ListenAndServeTLS(":8443", "./ca.crt", "./ca.key",nil)
if err != nil {
log.Fatalf("启动 HTTPS 服务器失败: %v", err)
}
}
然后启动这个 HTTPS 服务器:
go run server.php
客户端请求
接下来,我们在 https
目录下创建客户端调用代码 client.go
:
package main
import (
"crypto/tls"
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://127.0.0.1:8443/hello?name=学院君", nil)
if err != nil {
fmt.Printf("请求初始化失败:%v", err)
return
}
// 设置跳过不安全的 HTTPS
tls11Transport := &http.Transport{
MaxIdleConnsPerHost: 10,
TLSClientConfig: &tls.Config{
MaxVersion: tls.VersionTLS11,
InsecureSkipVerify: true,
},
}
client := &http.Client{
Transport: tls11Transport,
}
resp, err := client.Do(req)
if err != nil {
fmt.Printf("客户端发起请求失败:%v", err)
return
}
defer resp.Body.Close()
io.Copy(os.Stdout, resp.Body)
}
这里,我们将请求 URL 调整为 HTTPS 协议,并且自定义了客户端实例的 Transport
(更多底层细节可以参考上篇教程介绍)来跳过不安全的 HTTPS 连接验证。运行客户端代码,打印结果如下:
说明 HTTPS 请求成功。
当然以上 HTTP 服务和 HTTPS 服务通过浏览器访问也是可以的:
4 Comments
同样的URL,第一个是在golang编辑器下输入的,第二句是复制本篇文章的。
第一句手打的老是报
错误。复制第二句后正常。
Editor下的FileEncoding配置了三个utf-8
端口号不一样啊 你自己敲的端口号是 8843 不是 8443 ?
http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) 请问一下为什么第一个参数不是指针类型的...
学院君,你这写错了,应该是go run server.go go run server.php