
头条真实面试题
Go中的http包的使用?
Golang中http包中处理 HTTP 请求主要跟两个东西相关:ServeMux 和 Handler。
ServeMux 本质上是一个 HTTP 请求路由器(或者叫多路复用器,Multiplexor)。它把收到的请求与一组预先定义的 URL 路径列表做对比,然后在匹配到路径的时候调用关联的处理器(Handler)。
处理器(Handler)负责输出HTTP响应的头和正文。任何满足了http.Handler接口的对象都可作为一个处理器。通俗的说,对象只要有个如下签名的ServeHTTP方法即可:
ServeHTTP(http.ResponseWriter, *http.Request)
Go 语言的 HTTP 包自带了几个函数用作常用处理器,比如 FileServer
,NotFoundHandler
和 RedirectHandler
。
应用示例:
package mainimport ("log""net/http")func main() {mux := http.NewServeMux()rh := http.RedirectHandler("http://www.baidu.com", 307)mux.Handle("/foo", rh)log.Println("Listening...")http.ListenAndServe(":3000", mux)}
在这个应用示例中,首先在 main 函数中我们只用了 http.NewServeMux
函数来创建一个空的 ServeMux
。 然后我们使用 http.RedirectHandler
函数创建了一个新的处理器,这个处理器会对收到的所有请求,都执行307重定向操作到 http://www.baidu.com
。
接下来我们使用 ServeMux.Handle
函数将处理器注册到新创建的 ServeMux
,所以它在 URL 路径 /foo
上收到所有的请求都交给这个处理器。 最后我们创建了一个新的服务器,并通过 http.ListenAndServe
函数监听所有进入的请求,通过传递刚才创建的 ServeMux
来为请求去匹配对应处理器。
在浏览器中访问 http://localhost:3000/foo
,你应该能发现请求已经成功的重定向了。
此刻你应该能注意到一些有意思的事情:ListenAndServer
的函数签名是 ListenAndServe(addr string, handler Handler)
,但是第二个参数我们传递的是个 ServeMux
。
通过这个例子我们就可以知道,net/http
包在编写golang web应用中有很重要的作用,它主要提供了基于HTTP协议进行工作的client实现和server实现,可用于编写HTTP服务端和客户端。
net
import "net"
net包提供了可移植的网络I/O接口,包括TCP/IP、UDP、域名解析和Unix域socket。
虽然本包提供了对网络原语的访问,大部分使用者只需要Dial、Listen和Accept函数提供的基本接口;以及相关的Conn和Listener接口。crypto/tls包提供了相同的接口和类似的Dial和Listen函数。
Listen函数创建的服务端:
ln, err := net.Listen("tcp", ":8080")if err != nil {// handle error}for {conn, err := ln.Accept()if err != nil {// handle errorcontinue}go handleConnection(conn)}
Dial函数和服务端建立连接:
conn, err := net.Dial("tcp", "baidu.com:80")if err != nil {// handle error}fmt.Fprintf(conn, "GET HTTP/1.0\r\n\r\n")status, err := bufio.NewReader(conn).ReadString('\n')// ...
TCPConn
TCPConn代表一个TCP网络连接,实现了Conn接口。
Conn接口
Conn接口代表通用的面向流的网络连接。多个线程可能会同时调用同一个Conn的方法。
type Conn interface {// Read从连接中读取数据// Read方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真Read(b []byte) (n int, err error)// Write从连接中写入数据// Write方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真Write(b []byte) (n int, err error)// Close方法关闭该连接// 并会导致任何阻塞中的Read或Write方法不再阻塞并返回错误Close() error// 返回本地网络地址LocalAddr() Addr// 返回远端网络地址RemoteAddr() Addr// 设定该连接的读写deadline,等价于同时调用SetReadDeadline和SetWriteDeadline// deadline是一个绝对时间,超过该时间后I/O操作就会直接因超时失败返回而不会阻塞// deadline对之后的所有I/O操作都起效,而不仅仅是下一次的读或写操作// 参数t为零值表示不设置期限SetDeadline(t time.Time) error// 设定该连接的读操作deadline,参数t为零值表示不设置期限SetReadDeadline(t time.Time) error// 设定该连接的写操作deadline,参数t为零值表示不设置期限// 即使写入超时,返回值n也可能>0,说明成功写入了部分数据SetWriteDeadline(t time.Time) error}
栗子一(tcp)
tcp服务端
package mainimport ("fmt""net")func process(conn net.Conn) {defer conn.Close()for {buf := make([]byte, 512)n, err := conn.Read(buf)if err != nil {fmt.Println("read err:", err)return}fmt.Println("read:", string(buf[:n]))}}func main() {fmt.Println("server start...")listen, err := net.Listen("tcp", "0.0.0.0:8000")if err != nil {fmt.Println("listen failed, err:", err)return}for {conn, err := listen.Accept()if err != nil {fmt.Println("accept failed, err:", err)continue}go process(conn)}}
tcp客户端
package mainimport ("bufio""fmt""net""os""strings")func main() {conn, err := net.Dial("tcp", "localhost:8000")if err != nil {fmt.Println("err dialing:", err.Error())return}defer conn.Close()inputReader := bufio.NewReader(os.Stdin)for {input, _ := inputReader.ReadString('\n')trimedInput := strings.Trim(input, "\r\n")if trimedInput == "Q" {return}_, err := conn.Write([]byte(trimedInput))if err != nil {fmt.Println("err conn.write:", err)return}}}
栗子二(http)
封装一个http连接,请求百度
package mainimport ("fmt""io""net")func main() {conn, err := net.Dial("tcp", "www.baidu.com:80")if err != nil {fmt.Println("err dialing:", err.Error())return}defer conn.Close()msg := "GET HTTP/1.1\r\n"msg += "Host: www.baidu.com\r\n"msg += "Connection: close\r\n"// msg += "Connection: keep-alive\r\n"msg += "\r\n\r\n"_, err = io.WriteString(conn, msg)if err != nil {fmt.Println("io write string failed, err:", err)return}buf := make([]byte, 4096)for {count, err := conn.Read(buf)if err != nil {break}fmt.Println(string(buf[:count]))}}
net/http
import "net/http"
http包提供了HTTP客户端和服务端的实现。
Get、Head、Post和PostForm函数发出HTTP/ HTTPS请求。
resp, err := http.Get("http://example.com/")...resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)...resp, err := http.PostForm("http://example.com/form",url.Values{"key": {"Value"}, "id": {"123"}})
程序在使用完回复后必须关闭回复的主体。
resp, err := http.Get("http://example.com/")if err != nil {// handle error}defer resp.Body.Close()body, err := ioutil.ReadAll(resp.Body)// ...
要管理HTTP客户端的头域、重定向策略和其他设置,创建一个Client:
client := &http.Client{CheckRedirect: redirectPolicyFunc,}resp, err := client.Get("http://example.com")// ...req, err := http.NewRequest("GET", "http://example.com", nil)// ...req.Header.Add("If-None-Match", `W/"wyzzy"`)resp, err := client.Do(req)// ...
要管理代理、TLS配置、keep-alive、压缩和其他设置,创建一个Transport:
tr := &http.Transport{TLSClientConfig: &tls.Config{RootCAs: pool},DisableCompression: true,}client := &http.Client{Transport: tr}resp, err := client.Get("https://example.com")
Client和Transport类型都可以安全的被多个go程同时使用。出于效率考虑,应该一次建立、尽量重用。
ListenAndServe使用指定的监听地址和处理器启动一个HTTP服务端。处理器参数通常是nil,这表示采用包变量DefaultServeMux作为处理器。Handle和HandleFunc函数可以向DefaultServeMux添加处理器。
http.Handle("/foo", fooHandler)http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))})log.Fatal(http.ListenAndServe(":8080", nil))
要管理服务端的行为,可以创建一个自定义的Server:
s := &http.Server{Addr: ":8080",Handler: myHandler,ReadTimeout: 10 * time.Second,WriteTimeout: 10 * time.Second,MaxHeaderBytes: 1 << 20,}log.Fatal(s.ListenAndServe())
栗子server
import ("fmt""net/http")func Hello(w http.ResponseWriter, r *http.Request) {fmt.Println("Hello World.")fmt.Fprintf(w, "Hello World.\n")}func main() {http.HandleFunc("/", Hello)err := http.ListenAndServe("0.0.0.0:6000", nil)if err != nil {fmt.Println("http listen failed.")}}
栗子client
import ("fmt""io/ioutil""net/http")func main() {res, err := http.Get("http://www.baidu.com")if err != nil {fmt.Println("Get error:", err)return}data, err := ioutil.ReadAll(res.Body)if err != nil {fmt.Println("Get data error:", err)return}fmt.Println(string(data))}
栗子head
import ("fmt""net/http""time")var url = []string{"http://www.baidu.com","http://www.google.com","http://pan.263.net",}func main() {for _, v := range url {http.DefaultClient.Timeout = time.Second * 2resp, err := http.Head(v)if err != nil {fmt.Printf("head %s failed, err: %v\n", v, err)continue}fmt.Printf("head %s succ, status: %v\n", v, resp.Status)}}
栗子(form)
import ("fmt""io""log""net/http")const form = `<html><body><form action="#" method="post" name="bar"><input type="text" name="in"/><input type="text" name="in"/><input type="submit" value="Submit"/></form></body></html>`func SimpleServer(w http.ResponseWriter, request *http.Request) {io.WriteString(w, "Hello World.")}func FormServer(w http.ResponseWriter, request *http.Request) {w.Header().Set("Content-Type", "text/html")switch request.Method {case "GET":io.WriteString(w, form)case "POST":request.ParseForm()io.WriteString(w, request.Form["in"][1])io.WriteString(w, "\n")io.WriteString(w, request.FormValue("in"))}}func logPanics(handle http.HandlerFunc) http.HandlerFunc {return func(writer http.ResponseWriter, request *http.Request) {defer func() {if x := recover(); x != nil {log.Printf("[%v] caught panic: %v", request.RemoteAddr, x)}}()handle(writer, request)}}func main() {http.HandleFunc("/test1", SimpleServer)http.HandleFunc("/test2", logPanics(FormServer))err := http.ListenAndServe("0.0.0.0:6000", nil)if err != nil {fmt.Println("http listen failed.")}}
栗子(template)
package mainimport ("fmt""html/template""net/http")type Person struct {Title stringName stringAge int}func SimpleServer(w http.ResponseWriter, request *http.Request) {indexFailPath := "./index.html"t, err := template.ParseFiles(indexFailPath)if err != nil {fmt.Println("parse file err:", err)return}p := Person{Name: "Nick", Age: 18, Title: "Good."}if err = t.Execute(w, p); err != nil {fmt.Println("There was an error:", err.Error())return}}func main() {http.HandleFunc("/test1", SimpleServer)err := http.ListenAndServe("0.0.0.0:9000", nil)if err != nil {fmt.Println("http listen failed.")}}
<html><head><title>{{.Title}}</title></head><body><p>{{.Name}}</p>{{if gt .Age 18}}<p>MAN: {{.Name}}</p>{{else}}<p>Kid: {{.Name}}</p>{{end}}</body></html>
更多用法
not 非{{if not .condition}} {{end}}
and 与{{if and .condition1 .condition2}} {{end}}
or 或{{if or .condition1 .condition2}} {{end}}
eq 等于{{if eq .var1 .var2}} {{end}}
ne 不等于{{if ne .var1 .var2}} {{end}}
lt 小于 (less than){{if lt .var1 .var2}} {{end}}
le 小于等于{{if le .var1 .var2}} {{end}}
gt 大于{{if gt .var1 .var2}} {{end}}
ge 大于等于{{if ge .var1 .var2}} {{end}}
range 循环 {{range.}} {{end }}
Http 状态码
const (StatusContinue = 100StatusSwitchingProtocols = 101StatusOK = 200StatusCreated = 201StatusAccepted = 202StatusNonAuthoritativeInfo = 203StatusNoContent = 204StatusResetContent = 205StatusPartialContent = 206StatusMultipleChoices = 300StatusMovedPermanently = 301StatusFound = 302StatusSeeOther = 303StatusNotModified = 304StatusUseProxy = 305StatusTemporaryRedirect = 307StatusBadRequest = 400StatusUnauthorized = 401StatusPaymentRequired = 402StatusForbidden = 403StatusNotFound = 404StatusMethodNotAllowed = 405StatusNotAcceptable = 406StatusProxyAuthRequired = 407StatusRequestTimeout = 408StatusConflict = 409StatusGone = 410StatusLengthRequired = 411StatusPreconditionFailed = 412StatusRequestEntityTooLarge = 413StatusRequestURITooLong = 414StatusUnsupportedMediaType = 415StatusRequestedRangeNotSatisfiable = 416StatusExpectationFailed = 417StatusTeapot = 418StatusInternalServerError = 500StatusNotImplemented = 501StatusBadGateway = 502StatusServiceUnavailable = 503StatusGatewayTimeout = 504StatusHTTPVersionNotSupported = 505)





