The Http protocol (Hyper Text Transfer Protocol) is a simple request-response protocol that usually runs on TCP. The hypertext transmission protocol is the most widely used network transmission protocol on the Internet, and all WWW files must comply with this standard.
The Http protocol is based on client Cilent/server Server mode and is connected to the connection. Simply put, the client Cilent sends an http request Request to the server Server. After receiving the http service request Request, the server Server will return the requested data in the http response Response.
Built-in Go languagenet/http
The package is very excellent and provides the implementation of HTTP client and server.
Official website address:/net/http
1. HTTP client
Make HTTP/HTTPS requests through Get, Head, Post, and PostForm functions.
resp, err := ("/") resp, err := ("/upload", "image/jpeg", &buf) resp, err := ("/form",{"key": {"Value"}, "id": {"123"}})
1.1 Send a Get request
Without request parameters:
package main import ( "fmt" "io/ioutil" "net/http" ) func main() { resp, err := ("") if err != nil { ("get failed, err:%v\n", err) return } // The program must close the body of the reply after using the response defer () body, err := () if err != nil { ("read from failed, err:%v\n", err) return } (string(body)) }
With parameters, parameters need to be processed using net/url.
package main import ( "fmt" "io/ioutil" "net/http" "net/url" ) func main() { apiUrl := "http://127.0.0.1:9090/get" // URL param data := {} ("name", "mi") ("age", "18") u, err := (apiUrl) if err != nil { ("parse url requestUrl failed, err:%v\n", err) } // URL encode = () // http://127.0.0.1:9090/get?age=18&name=mi (()) resp, err := (()) if err != nil { ("post failed, err:%v\n", err) return } defer () b, err := () if err != nil { ("get resp failed, err:%v\n", err) return } // {"status": "ok"} (string(b)) }
Processing on the corresponding server side:
package main import ( "fmt" "net/http" ) func getHandler(w , r *) { defer () data := () // mi (("name")) // 18 (("age")) answer := `{"status": "ok"}` ([]byte(answer)) } func main() { ("/get", getHandler) err := (":9090", nil) if err != nil { ("http server failed, err:%v\n", err) return } }
Header header information:
package main import ( "fmt" "net/http" "net/http/httputil" ) func main() { url := "http://127.0.0.1:9090/get" request, err := (, url, nil) if err != nil { panic(err) } ("Authorization", "jhs8723sd2dshd2") ("User-Agent", "User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1") resp, err := (request) if err != nil { panic(err) } defer () // // Get web page content s, err := (resp, true) if err != nil { panic(err) } ("%s", s) }
1.2 Send a POST request
package main import ( "fmt" "io/ioutil" "net/http" "strings" ) func main() { url := "http://127.0.0.1:9090/post" // Form data // contentType := "application/x-www-form-urlencoded" // data := "name=mi&age=18" // json contentType := "application/json" data := `{"name":"mi","age":18}` resp, err := (url, contentType, (data)) if err != nil { ("post failed, err:%v\n", err) return } defer () b, err := () if err != nil { ("get resp failed, err:%v\n", err) return } // {"status": "ok"} (string(b)) }
Corresponding server side:
package main import ( "fmt" "io/ioutil" "net/http" ) func postHandler(w , r *) { defer () // 1. When the request type is application/x-www-form-urlencoded, parses form data () // Print form data // map[] () (("name"), ("age")) // 2. When the request type is application/json, data is read from b, err := () if err != nil { ("read failed, err:%v\n", err) return } // {"name":"mi","age":18} (string(b)) answer := `{"status": "ok"}` ([]byte(answer)) } func main() { ("/post", postHandler) err := (":9090", nil) if err != nil { ("http server failed, err:%v\n", err) return } }
1.3 PostForm method
package main import ( "fmt" "io/ioutil" "net/http" "net/url" ) func main() { resp, err := ("http://127.0.0.1:9090/post", {"key": {"Value"}, "id": {"123"}}) if err != nil { (err) } defer () body, err := () if err != nil { (err) } (string(body)) }
json:
package main import ( "bytes" "encoding/json" "fmt" "io/ioutil" "net/http" ) func main() { data := make(map[string]interface{}) data["site"] = "" data["name"] = "tom" bytesData, _ := (data) resp, _ := ("/post", "application/json", (bytesData)) body, _ := () (string(body)) }
With headers parameters:
package main import ( "fmt" "io/ioutil" "net/http" "strings" ) func main() { client := &{} req, err := ("POST", "http:///demo/", ("name=cjb")) if err != nil { (err) } ("Content-Type", "application/x-www-form-urlencoded") ("Cookie", "name=anny") resp, err := (req) defer () body, err := () if err != nil { (err) } (string(body)) }
1.4 Custom Client
To manage the header domain, redirect policies, and other settings of an HTTP client, create a Client:
package main import ( "fmt" "net/http" "net/http/httputil" ) func main() { request, err := (, "", nil) if err != nil { panic(err) } client := { CheckRedirect: func(req *, via []*) error { ("Redirect:", req) return nil }, } ("User-Agent", "User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1") resp, err := (request) if err != nil { panic(err) } defer () s, err := (resp, true) ("%s", s) }
1.5 Custom Transport
To manage proxy, TLS configuration, keep-alive, compression, and other settings, create a Transport:
package main import ( "crypto/tls" "crypto/x509" "fmt" "io/ioutil" "net/http" ) // Use matching certificate methodfunc main() { // Contents of *.pem file rootCA := "" roots := () ok := ([]byte(rootCA)) if !ok { panic("failed to parse root certificate") } client := &{ Transport: &{ TLSClientConfig: &{RootCAs: roots}, DisableCompression: true, }, } resp, err := ("") if err != nil { panic(err) } defer () body, err := () if err != nil { panic(err) } if err != nil { ("read from failed, err:%v\n", err) return } (string(body)) }
Both Client and Transport types can be safely used by multiple goroutines at the same time. For efficiency considerations, it should be established at one time and reused as much as possible.
package main import ( "crypto/tls" "fmt" "io/ioutil" "net/http" ) // Skip the security checkfunc main() { tr := &{ TLSClientConfig: &{InsecureSkipVerify: true}, } client := &{Transport: tr} resp, err := ("https://localhost:8081") if err != nil { ("error:", err) return } defer () body, err := () (string(body)) }
2. Server side
2.1 Default Server
ListenAndServe starts an HTTP server using the specified listening address and processor. The processor parameter is usually nil, which means that the packet variable is used.
DefaultServeMux as processor.
The Handle and HandleFunc functions can add processors to DefaultServeMux.
package main import ( "log" "net/http" ) type httpServer struct { } func (server httpServer) ServeHTTP(w , r *) { ([]byte()) } func main() { var server httpServer ("/foo", server) ("/bar", func(w , r *) { ([]byte()) }) ((":9090", nil)) }
The Handler interface of the event handler is defined as follows:
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
As long as this interface is implemented, you can implement your own handler processor.
If the first parameter address passed by ListenAndServe() is empty, the server uses it by default after startuphttp://127.0.0.1:8080
Address for access.
If the second parameter passed in by this function is nil, the server will use the default multiplexer after startupDefaultServeMux
。
2.2 Default Server Example
Use the Go languagenet/http
Package to write a simple server-side example that receives HTTP requests.net/http
The package is for net package
Further encapsulation, specially used to process data from the HTTP protocol. The specific code is as follows:
package main import ( "fmt" "net/http" ) func sayHello(w , r *) { (w, "Hello shanghai!") } func main() { ("/", sayHello) err := (":9090", nil) if err != nil { ("http server failed, err:%v\n", err) return } }
After compiling the above code, execute it, open the browser on your computer and enter it in the address bar.127.0.0.1:9090
Enter:
Output
Hello shanghai!
package main import ( "encoding/json" _ "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "strings" ) var ( api = "http://127.0.0.1:9090/" token = "9adf92861" ) type Result struct { Code int `json:"code"` Msg string `json:"msg"` } type Resp struct { Code string `json:"code"` Msg string `json:"msg"` } func wechatUrlHandle(w , r *) { // Get the link address var wechatUrl string values := () wechatUrl = ("url") // Verify the correctness of the URL targetUrl, err := (wechatUrl) if err != nil { ("parse url failed, err:%v\n", err) } (("url: %v", targetUrl)) // Initiate a request for splicing parameters data := ("token=%s&url=%s", token, ()) headers := "application/x-www-form-urlencoded" res, err := (api, headers, (data)) if err != nil { ("post failed, err: %v\n", err) return } // Close the request resource defer () body, err := () if err != nil { (w, ("get resp failed. err: %v\n", err)) return } var info Result var ress Resp ().Set("Content-Type", "application/json") if err := (body, &info); err == nil { (, ) = "200" = "Success" t, _ := (ress) (t) } else { = "200" = "failure" t, _ := (ress) ("err: ", err) (t) } } func main() { ("/wechat/url", wechatUrlHandle) // Processor parameters are generally nil err := (":9090", nil) if err != nil { ("Http server failed. err: %v\n", err) return } }
2.3 Custom Server
To manage server behavior, you can create a custom server.
Users can configure the server in more detail through the Server structure, including setting the address, setting the time exceeds the time for the requested read operation, etc.
package main import ( "log" "net/http" "time" ) type httpServer struct { } func (server httpServer) ServeHTTP(w , r *) { ([]byte()) } func main() { s := &{ Addr: ":9090", Handler: httpServer{}, ReadTimeout: 10 * , WriteTimeout: 10 * , MaxHeaderBytes: 1 << 20, } (()) }
2.4 Multiplexer
The basic principle of multiplexer: find the corresponding processor based on the requested URL address and call the corresponding processor.ServeHTTP()
Method handles the request.
DefaultServeMux is the default multiplexer for the net/http package, and it is actually an instance of ServeMux.
The HandleFunc() function is used to register a processor for the specified URL. The HandleFunc() processor function internally calls the HandleFunc method corresponding to the DefaultServeMux object.
2.4.1 ServeMux and DefaultServeMux
We can register multiple processors using the default multiplexer to achieve the same function as the processor.
The following shows how to create your own server with the default multiplexer.
package main import ( "fmt" "net/http" ) //Define multiple processorstype handle1 struct{} func (h1 *handle1) ServeHTTP(w , r *) { (w, "handle one") } type handle2 struct{} func (h2 *handle2) ServeHTTP(w , r *) { (w, "handle two") } func main() { handle1 := handle1{} handle2 := handle2{} // Handler:nil indicates that the server uses the default multiplexer DefaultServeMux s := &{ Addr: "127.0.0.1:8080", Handler: nil, } // The Handle function calls the multiplexer method ("/handle1", &handle1) ("/handle2", &handle2) () }
We specify two processors by using our own handle1 and handle2. The () function can call the () method to handle the request.
Handle: nil The corresponding processor is DefaultServeMux.
In the ServeHTTP() method of the ServeMux object, find the server we registered according to the URL and then request it to handle.
Although the default multiplexer is very useful, it is still not recommended because it is a global variable and all code can modify it. Some third-party libraries may conflict with the default multiplexer. So the recommended way is customization.
2.4.2 Custom Multiplexer
package main import ( "fmt" "net/http" ) func newservemux(w , r *) { (w, "NewServeMux") } func main() { mux := () ("/", newservemux) s := &{ Addr: ":8081", Handler: mux, } () }
NewServeMux is essentially ServeMux.
2.4.3 Routing matching of ServeMux
We now need to bind three URLs: /, /happy, /bad.
package main import ( "fmt" "net/http" ) func newservemux(w , r *) { (w, "NewServeMux") } func newservemuxhappy(w , r *) { (w, "Newservemuxhappy") } func newservemuxbad(w , r *) { (w, "NewServeMuxbad") } func main() { mux := () ("/", newservemux) ("/happy", newservemuxhappy) ("/bad", newservemuxbad) s := &{ Addr: ":8080", Handler: mux, } () }
2.5 HttpRouter
One of the shortcomings of ServeMux is that it cannot use variables to implement URL pattern matching, while HttpRouter can, HttpRouter is a high-performance
The three-party HTTP routing packets make up for the insufficient routing problem in the net/http packets.
$ go get /julienschmidt/httprouter
To use httprouter, you must first use the New() function to generate a *Router routing object, and then use GET() to register a matching function
, and finally pass this parameter into the function to listen to the service.
package main import ( "net/http" "/julienschmidt/httprouter" ) func Hello(w , r *, _ ) { ([]byte("Hello,httprouter!")) } func main() { router := () ("/", Hello) (":8080", router) }
More importantly, it provides two matching patterns for URLs:
-
/user/:pac
: Accurate matching /user/pac -
/user/*pac
: Match all patterns /user/hello
Pattern: /user/:user /user/gordon match /user/you match /user/gordon/profile no match /user/ no match
Pattern: /src/*filepath /src/ match /src/ match /src/subdir/ match
package main import ( "fmt" "log" "net/http" "/julienschmidt/httprouter" ) func Index(w , r *, _ ) { (w, "Welcome!\n") } func Hello(w , r *, ps ) { (w, "hello, %s!\n", ("name")) } func main() { router := () ("/", Index) ("/hello/:name", Hello) ((":8080", router)) }
Of course, there are details of the POST() and DELETE() functions here, so I won't introduce them one by one.
3. Other applications
3.1 DNS
package main import ( "fmt" "log" "net" ) func main() { // Return to IP address according to domain name addr, err := ("ip", "") if err != nil { return } // 2023/07/24 20:45:43 Corresponding address ip address----> 172.21.6.96 (" Corresponding address ip address---->", addr) // Find DNS A records iprecords, _ := ("") for _, ip := range iprecords { // LookupIP -----> 172.21.6.96 ("LookupIP ----->", ip) } //Find DNS CNAME records canme, _ := ("") // LookupCNAME -----> . ("LookupCNAME ----->", canme) //Find DNS PTR records ptr, e := ("172.21.6.96") if e != nil { // lookup 172.21.6.96: dnsquery: DNS name does not exist. (e) } else { (ptr) } for _, ptrval := range ptr { (ptrval) } //Find DNS NS records Name server records nameserver, _ := ("") for _, ns := range nameserver { /* ns record &{.} ns record &{.} ns record &{.} ns record &{.} ns record &{.} */ ("ns record", ns) } //Find DNS MX records Mail server records mxrecods, _ := ("") for _, mx := range mxrecods { ("mx:", mx) } //Find the text information corresponding to the DNS TXT record txtrecords, _ := ("") for _, txt := range txtrecords { /* txt: _globalsign-domain-verification=qjb28W2jJSrWj04NHpB0CvgK9tle5JkOq-EcyWBgnE txt: google-site-verification=GHb98-6msqyx_qqjGl5eRatD3QTHyVB6-xQ3gJB5UwM txt: v=spf1 include: include: include: include: mx ptr -all */ ("txt:", txt) } // Check the local IP address addrs, err := () if err != nil { return } for _, address := range addrs { // Check the IP address to determine whether the loopback address is if ipnet, ok := address.(*); ok && !() { if .To4() != nil { /* Local address --> 169.254.124.27 Local address --> 169.254.27.66 Local address --> 169.254.158.118 Local address --> 192.168.223.1 Local address --> 192.168.132.1 Local address --> 192.168.226.185 */ ("Local address -->", ()) } } } }
3.2 Simple tcp request implementation
3.2.1 Server
package main import ( "bufio" "io" "log" "net" ) func pes(conn ) { defer () read := (conn) for { var p [512]byte n, err := (p[:]) if err != nil { if err == { ("Client Close Link", ().String()) return } ("Read failed", ()) return } ("The client sends the message as", string(p[:n])) ([]byte("ok")) } } func main() { lis, err := ("tcp", "127.0.0.1:20003") if err != nil { ("listen ,err ", ()) return } for { conn, err := () if err != nil { ("Create link exception", ()) } go pes(conn) } }
3.2.2 Client
package main import ( "bufio" "io" "log" "net" "os" "strings" ) func main() { conn, err := ("tcp", "127.0.0.1:20003") if err != nil { ("conn err", ()) return } defer () inputreader := () for { input, _ := ('\n') input = (input, "\r\n") if input == "q" || input == "Q" { ("Exit Link") return } ([]byte(input)) var p [512]byte n, err := (p[:]) if err != nil { if err == { ("Server-side Close Link", ().String()) return } ("Read failed", ()) return } ("The server sends a message as", string(p[:n])) } }
3.3 Sticking problem
Only fixed-length data can be read at a time, solution:
package main import ( "bufio" "bytes" "encoding/binary" "fmt" ) func Encode(massge string) ([]byte, error) { len_for_msaaget := int32(len(massge)) pkg := new() err := (pkg, , len_for_msaaget) if err != nil { return nil, err } err = (pkg, , []byte(massge)) if err != nil { return nil, err } return (), nil } func Decode(reader *) (string, error) { peek, err := (4) if err != nil { return "", err } lengthBuff := (peek) var length int32 err = (lengthBuff, , &length) if err != nil { return "", err } if int32(()) <= 4+length { return "", ("err") } p := make([]byte, int(4+length)) _, err = (p) if err != nil { return "", err } return string(p[4:]), nil }
3.4 Simple udp request implementation
3.4.1 Server
package main import ( "log" "net" ) func main() { udp, err := ("udp", &{ IP: net.IPv4(0, 0, 0, 0), Port: 30000, }) if err != nil { return } defer () for { var p [1024]byte n, addr, err := (p[:]) if err != nil { ("UDP request handling exception", ()) return } ("Request information", string(p[:n]), ()) _, err = ([]byte("get"), addr) if err != nil { ("Return information failed", ()) return } } }
3.4.2 Client
package main import ( "log" "net" ) func main() { udp, err := ("udp", nil, &{ IP: net.IPv4(0, 0, 0, 0), Port: 30000, }) if err != nil { (()) return } defer () _, err = ([]byte("I'm a server, hello server")) if err != nil { (()) return } data := make([]byte, 4096) fromUDP, u, err := (data) if err != nil { ("Receive failed", err) return } ("Received information returned successfully", string([]byte(data[:fromUDP])), u) }
This is the end of this article about the detailed explanation of the use of net/http packages in Go. For more related Go net/http package content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!