使用Go语言开发网站的时候我们会使用到内置的net/http包,该包提供了HTTP客户端和服务端的实现。这里我们主要使用该包提供的方法创建一个GO web的服务端。
构建服务端,我们使用到http.Handle方法,里面需要提供一个路由和一个处理的函数。提供的函数里面的参数是固定的,请看下面代码演示:
import (
"fmt"
"io/ioutil"
"net/http"
)
func index(w http.ResponseWriter, r *http.Request) {
//Fprint() 与Fprintln() 按照默认格式将内容写入文件并返回内容是写入的字节与错误。
len, err := fmt.Fprint(w, "w是响应写入,即返回给客户端的内容")
if err != nil {
fmt.Println("Fprint向w写入内容出错:", err)
} else {
fmt.Println("Fprint向w写入", len, "个字节")
}
}
func main() {
http.HandleFunc("/", index) //http根据用户请求路径绑定处理函数
//下面使用匿名函数请求1个远程网页将在本地返回(类似代理)
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
//先使用http.Get方法请求一个网页(爬虫)
resp, err := http.Get("http://www.55mx.com/")
if err != nil {
fmt.Println("http.Get请求错误:", err)
}
defer resp.Body.Close() //客户必须在完成后关闭响应主体
//读取resp.Body里的内容
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("ReadAll读取resp.Body失败:", err)
}
fmt.Fprintln(w, string(body)) //将网页内容转为
//b, _ := ioutil.ReadFile("./1.txt")//读取本地文件
//fmt.Fprint(w, string(b))//将文件里的内容解析
//fmt.Fprintln(w, "请求路径:", html.EscapeString(r.URL.Path))
})
//监听并启动web服务
err := http.ListenAndServe(":8000", nil)
if err != nil {
fmt.Printf("http 服务启动失败, 错误:%vn", err)
return
}
}
Get,Head,Post和 PostForm 发出 HTTP(或HTTPS)请求:
resp, err := http.Get("http://www.55mx.com/")//通过GET请求一个地址
//通过POST请求一个地址
resp, err := http.Post("http://www.55mx.com/", "image/jpeg", &buf)
//通过postForm向一个地方发送数据
resp, err := http.PostForm("http://www.55mx.com/",
url.Values{"name": {"网络人"}, "url": {"55mx.com"}})
我们一般使用HandleFunc就可以了,如果想要使用handle相对来说就要麻烦一点,因为Handler的第二个参数是Handler这个的接口的ServeHTTP()方法。
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
所以这个方法使用的时候需要自己去定义struct实现这个Handler接口。关于接口实现详细介绍:http://www.55mx.com/go/79.html
//定义一个httpServer的结构体
type myServer struct {
}
//实现接口ServeHTTP
func (myServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(r.URL.Path)) //使用ResponseWriter的Write方法把URL路径显示在网页上
}
func main() {
var server myServer //server的类型是myServer
var mux = http.NewServeMux() //创建一个ServeMux实例
mux.Handle("/", server) //server实现了ServeHTTP方法即实现Handle的接口
//启动web服务,如果遇到err输出错误日志并退出程序
log.Fatal(http.ListenAndServe("localhost:8000", mux))
//ListenAndServe第二个参数为mux即使用mux定义的内容处理请求
}
下面我们重写Server来实现自己的相关功能。
import (
"io"
"log"
"net/http"
"time"
)
type myHandle struct{}
func (*myHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if h, ok := mux[r.URL.String()]; ok {
h(w, r) //找到/hello并调用路由方法sayHello(w, r)
return
}
io.WriteString(w, "没有绑定方法将执行这个,URL:"+r.URL.String()) //没有找到的路由只打印URL
}
func sayHello(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "sayHello(w,r):这是访问/hello得到的结果哦~")
}
func index(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "这是首页哦index(w,r)~")
}
var mux map[string]func(http.ResponseWriter, *http.Request) //定义一个map
func main() {
server := http.Server{
Addr: ":8000",
Handler: &myHandle{},
ReadTimeout: 5 * time.Second,
}
mux = make(map[string]func(http.ResponseWriter, *http.Request))
mux["/hello"] = sayHello
mux["/"] = index //路由绑定方法
err := server.ListenAndServe()
if err != nil {
log.Fatal(err)
}
}
func main() {
mux := http.NewServeMux()
wd, err := os.Getwd() //获取当前目录
if err != nil {
log.Fatal(err) //读取目录出错
}
//Handle处理/static/路径时调用StripPrefix方法
mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(wd))))
//StripPrefix遇到/static/开始的地址返回 FileServer(http.Dir(wd))里的内容
//http.FileServer(http.Dir("img/css")) 返回一个使用 FileSystem 接口 root 提供文件访问服务的 HTTP 处理器。可以方便的实现静态文件服务器。
//http.Dir("img/css")实现了FileSystem 的接口Open方法,返回的是 http.Dir 类型,将字符串路径转换成文件系统。
err = http.ListenAndServe(":8000", mux)
if err != nil {
log.Fatal(err)
}
}
这里需要使用到 r *http.Request里的方法与字段获取到用户请求的相关header信息和通过PUT、POST提交的数据。
//getHead 获取请求头里的信息
func getHead(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "地址:", r.URL.Path)
fmt.Fprintln(w, "?后面的查询字符串:", r.URL.RawQuery)
fmt.Fprintln(w, "所有的Header头信息:", r.Header) //将返回一个map[string][]string类型
fmt.Fprintln(w, "Accept-Encoding信息:", r.Header["Accept-Encoding"])
fmt.Fprintln(w, "获取没有[]的值:", r.Header.Get("User-Agent")) //Get方法直接返回值
}
//getBody 获取请求体信息(用户POST过来的数据)
func getBody(w http.ResponseWriter, r *http.Request) {
len := r.ContentLength //获取请求体的长度
body := make([]byte, len) //定义一个接收的切片
r.Body.Read(body) //将请求体里的内容读到body里
fmt.Fprintln(w, "请求体中的内容:", string(body))
r.ParseForm() //解析读取到的表单字段到r.Form与r.PostForm里
//ur.Values类型是form解析好的表单数据,包括query参数和POST、PUT的表单数据
fmt.Fprintln(w, "请求URL参数:", r.Form) //r.Form返回map[string][]string类型GET的URL?参数
fmt.Fprintln(w, "POST字段信息:", r.PostForm) //r.PostForm返回用户POST、PUT方法过来的字段
//解析POST的指定字段、r.PostForm返回用户POST、PUT方法过来的字段
fmt.Fprintln(w, "POST字段username:", r.PostForm["username"])
//当表单enctype属性为multipart/form-data(文件上传)的时候需要使用MultiPartForm读取数据
fmt.Fprintln(w, "接收multipart:", r.MultipartForm)
//不需要执行r.ParseForm()就可以使用下面方
fmt.Fprintln(w, "快速获取URL参数GET字段:", r.FormValue("username"))
fmt.Fprintln(w, "快速获取POST的表单字段:", r.PostFormValue("username"))
}
func main() {
http.HandleFunc("/getheadinfo", getHead)
http.HandleFunc("/getbody", getBody)
http.ListenAndServe(":8000", nil)
}
上面已经使用了很多次的w http.ResponseWriter相关字段与方法,r是用户传过来的,w就是我们服务器代码给用户的数据。我们可以通过w设置并自定义哪些数据、以什么样的方法写入到用户的浏览器里。
//respJSON 返回自定义的header与内容
func respJSON(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") //以json格式返回给浏览器
w.Header().Set("Location", "http://www.55mx.com") //WriteHeader设置302、301将会跳转到url上
w.Header().Set("Content-Length", "999") //Content-Length不要设置啊,会导致空白页,此项由服务器自动计算长度
w.WriteHeader(500) //返回一个状态码:200是正常
json, _ := json.Marshal(map[string]string{
"func": "这里直接使用Write方法返回数据",
}) //Marshal返回的是[]byte切片
w.Write(json) //w.Write的接收一个[]byte切片哦~
}
func main() {
//GO HTTP严格区分大小写/respJSON将返回404
http.HandleFunc("/respjson", respJSON)
http.ListenAndServe(":8000", nil)
}
GO语言在IO请求和管理上太强大了,这也是我喜欢GO,并学习的驱动力。早期做PHP开发的时候没有缓存,由于PHP是解释型语言,每次运行都需要对代码解释一次,同时配置文件也会重新读取一次,为了应对大并发,减少IO我们通常把配置通过redis等内存型数据库保存到内存里读取使用。GO的优势在这里就可以充分发挥出来。
config, _ := ioutil.ReadFile("config.txt") //只在程序运行时读取一次并加载到内存后续所有的处理器(Handle)都可调用
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
content, _ := ioutil.ReadFile("content.txt") //用户请求一次读一次,高并发时硬盘IO压力大
content = append(content, config...)
w.Write(content)
})
除非注明,网络人的文章均为原创,转载请以链接形式标明本文地址:https://www.55mx.com/post/92
《GO web笔记1:使用net/http包构建服务端》的网友评论(0)