GoWeb learning notes
The learning materials come from the video of teacher Yang Xu at station B
Create the first Web program (Web page output HelloWorld)
To create a HelloWorld output in a web page in go language, you need to create an access function first, and then specify the corresponding listening and service
HadleFunc source code
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) }
Use HadleFunc and create an access function in the form of a built-in function
//HandleFunc has two parameters. The first parameter is the access path and the second parameter is the routing function http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { writer.Write([]byte("Hello World!")) })
Use HadleFunc and create an access function by calling an external function
//Custom external functions func MyHandleFunc(w http.ResponseWriter,r *http.Request){ w.Write([]byte("MyHandleFunc")) } func main(){ //Call function http.HandleFunc("/myHandleFunc",MyHandleFunc) }
Create access monitoring and services
There are two ways to create access listener and service. One is to call http.ListenAndServe method, which requires two parameters to be configured, and the other is to call http.Server. This method requires self-defined parameters provided by http.Server. Compared with http.ListenAndServe, this method is more flexible
http.ListenAndServe source code
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() }
http.Server source code (removed the comments in the source code)
type Server struct { Addr string Handler Handler TLSConfig *tls.Config ReadTimeout time.Duration ReadHeaderTimeout time.Duration WriteTimeout time.Duration IdleTimeout time.Duration MaxHeaderBytes int TLSNextProto map[string]func(*Server, *tls.Conn, Handler) ConnState func(net.Conn, ConnState) ErrorLog *log.Logger BaseContext func(net.Listener) context.Context ConnContext func(ctx context.Context, c net.Conn) context.Context inShutdown atomicBool disableKeepAlives int32 nextProtoOnce sync.Once nextProtoErr error mu sync.Mutex listeners map[*net.Listener]struct{} activeConn map[*conn]struct{} doneChan chan struct{} onShutdown []func() }
Create listeners and services using http.ListenAndServe
//Using nil is equivalent to using HTTP. Defaultservermux (multiplexer) built in go language //Two parameters need to be passed in, namely, the access address and the access function used //When using localhost, it can be written as http.ListenAndServe(":8080",nil) http.ListenAndServe("localhost:8080",nil)
Create listener and service using http.Server
//It is equivalent to http.ListenAndServe, but it is more flexible to configure in this way because more parameter values can be set server := http.Server{ Addr: "localhost:8080", Handler: nil, } server.ListenAndServe()
Integration implementation
Create directly
//The access path is localhost:8080 http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { writer.Write([]byte("Hello World!")) }) http.ListenAndServe("localhost:8080",nil)
Implemented through external functions and using http.Server
//For convenience, a monitoring function is written func Listen(){ server := http.Server{ Addr: "localhost:8080", Handler: nil, } server.ListenAndServe() } //Custom access function func MyHandleFunc(w http.ResponseWriter,r *http.Request){ //Because the source code of the Write() function is Write([]byte) (int, error), it needs to be converted when outputting string type w.Write([]byte("Hello World")) } func main(){ http.HandleFunc("/myHandleFunc",MyHandleFunc) Listen() }
http.Handle source code
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
Create a Hello World using http.Handle
You can see from the source code of http.Handle that the second parameter passed in is Handler. The source code is as follows:
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
Handler is a Type of ServeHTTP, so the second parameter passed in to Handle must also be an accessor of ServeHTTP Type
//Custom routing type HelloHandler struct {} //Custom routing type AboutHandler struct {} //Define access controller func (m *HelloHandler) ServeHTTP(w http.ResponseWriter ,r *http.Request) { w.Write([]byte("Hello World!")) } //Define access controller func (m *AboutHandler) ServeHTTP(w http.ResponseWriter ,r *http.Request) { w.Write([]byte("About!")) } func MyListen(){ mh := HelloHandler{} ma := AboutHandler{} server := http.Server{ Addr: ":8080", //Do not specify an access router, so as to access different custom routers by accessing different routing parameters Handler: nil, } http.Handle("/hello",&mh) http.Handle("/about",&ma) server.ListenAndServe() }
be careful
When using the Handle function above, the created servehttp is used. We can also use HandlerFunc to convert the created route that does not inherit servehttp
//Use HandlerFunc to convert custom access control into a Handler //HandlerFunc can adapt a function f with appropriate signature to a Handler http.Handle("/my", http.HandlerFunc(MyHandleFunc)) func MyHandleFunc(w http.ResponseWriter, r *http.Request) { w.Write([]byte("MyHandleFunc")) }
Five built-in handlers for Go language
1.NotFoundHandler
A handler is returned, and the corresponding of each request is 404 page not found
func NotFoundHandler() Handler { return HandlerFunc(NotFound) }
2.RedirectHandler
Return a handler and jump each request to the specified URL according to the status code
Common: statusmoved permanently, StatusFound, StatusSeeOther
func RedirectHandler(url string, code int) Handler { return &redirectHandler{url, code} }
3.StripPrefix
To prefix, return a handler, remove the prefix in the specified url, and call another handler.
func StripPrefix(prefix string, h Handler) Handler { if prefix == "" { return h } return HandlerFunc(func(w ResponseWriter, r *Request) { if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) { r2 := new(Request) *r2 = *r r2.URL = new(url.URL) *r2.URL = *r.URL r2.URL.Path = p h.ServeHTTP(w, r2) } else { NotFound(w, r) } }) }
4.TimeoutHandler
Returns a handler to run the incoming handler within the specified time
func TimeoutHandler(h Handler, dt time.Duration, msg string) Handler { return &timeoutHandler{ handler: h, body: msg, dt: dt, } }
5.FileServer
Returns a handler that uses the root based file system to handle the request
func FileServer(root FileSystem) Handler { return &fileHandler{root} }
type FileSystem interface { Open(name string) (File, error) }
The file system of the operating system needs to be used, so it is generally handed over to the following functions
type Dir string
func (d Dir) Open(name string) (File, error) { if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) { return nil, errors.New("http: invalid character in file path") } dir := string(d) if dir == "" { dir = "." } fullName := filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name))) f, err := os.Open(fullName) if err != nil { return nil, mapDirOpenError(err, fullName) } return f, nil }
HTTP message
HTTP Request and HTTP Response have the same structure. They both have request lines, 0 or more URLs, empty lines and optional message bodies
Request
In GO language, Request is a struct, which represents the HTTP Request message sent by the client (it can represent both the client's Request and the server's Request). You can access the Cookie, URL, User Agent and other information in the Request through the Request method. The source code is as follows:
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 }
Several important fields
1.URL
The URL field of Request represents part of the content in the Request line (the first line of Request information). The URL field is a pointer to the url.URL type. url.URL is a struct, and the source code is as follows:
type URL struct { Scheme string Opaque string User *Userinfo Host string Path string RawPath string ForceQuery bool RawQuery string Fragment string RawFragment string }
The common format of URL is:
scheme://[userinfo@]host/path[?query][#fragment]
URL s that do not begin with a slash are interpreted as:
scheme:opaque[?query][#fragment]
URL Query
- RawQuery provides the string of the actual query
- Through the Form field of the Request
r.URL.Query()
This method will provide the map[string] [] string corresponding to the query string
URL Fragment
This is the # following part of the URL format
When the request is sent from the browser, the value of Fragment cannot be obtained, because the Fragment will be removed when the browser sends the request
The request sent by some client tools can obtain the value of Fragment, such as HTTP client package
2.Handler
The request and corresponding headers are described by Header type, which is a map type used to describe the key value pair in the HTTP header.
The key of Header map is string type, and the value is [] string
When setting the key, an empty [] string will be created as the value, and the first element in the value is the value of the new header
If you want to add a new header value for the specified key, just execute the append operation
3.Body
The request and the corresponding bodies are represented by the Body field
The Body is an io.ReadCloser interface, a Reader interface and a Closer interface
The Reader interface defines an Open method with the parameter [] byte, which returns the number of bytes and optional errors
The Closer interface defines a Close method: there are no parameters and an optional error is returned
Read the contents of the body and call the read method of the body
func getQuery(){ http.HandleFunc("/query", func(writer http.ResponseWriter, request *http.Request) { //Get URL url := request.URL //Call the query method query := url.Query() //Query the corresponding data according to the passed in key value and return all values id := query["id"] //Print as a log on the console log.Println(id) //Returns the first value according to the passed in key value name := query.Get("name") log.Println(name) }) }
4.Form,PostForm,MultipartForm
Send a post request through a form
<form action="/index" method="post"> user name<input type="text" name="name" /> password<input type="password" name="password" /> <input type="submit" /> </form>
action is the server path corresponding to the sending request, and method is the way to send the request, including post and get. The data in the html form will be sent through the post request in the form of name value pair.
name-value
The format of the name value data pair sent through psot is specified by the Content Type of the form, that is, the enctype attribute in the form. In the form form, the default attribute of enctype is application/x-www-form-urlencoded.
1.application/x-www-form-urlencoded
Under this attribute, the browser will encode the form data into the query string, which is used for simple text format
2.multipart/form-data
Under this attribute, each name value pair is converted into a mime message part
Each part has its own Content Type and Content Disposition. This method is used when uploading files
3.text/plain
POST & GET
The method attribute of the form can set post and get attributes
1.GET
The get request has no body, and all data is sent through the name value pair of the URL
2.POST
FORM field
The function on Resquest allows us to extract data from url or / and body. The data in the form is a key value pair
The usual approach is to call ParseForm or ParseMultipartForm to resolve the Request, and then access the Form, PostForm or MultipartForm fields accordingly
PostForm
PostForm only supports application/x-www-form-urlencoded,
func getForm(){ http.HandleFunc("/process", func(writer http.ResponseWriter, request *http.Request) { request.ParseForm() //Output to page fmt.Fprintln(writer,request.Form) //Print to the console as a log log.Println(request.Form) }) }
Front end code:
<form action="http://localhost:8080/process" method="post" > user name<input type="text" name="name" /> password<input type="password" name="password" /> <input type="submit" /> </form>
MultipartForm
When using MultipartForm, you need to call ParseMultipartForm first. ParseMultipartForm will call ParseForm when necessary. You need to pass in a parameter (read data length, in bytes). MultipartForm only contains the key value pair of the form. The return type is a struct instead of a map. This struct contains two maps, One is the data in your form, the other is the file
func getMultipart(){ http.HandleFunc("/process", func(writer http.ResponseWriter, request *http.Request) { //The parameter is byte, which is the length of uploaded data request.ParseMultipartForm(1024) fmt.Fprintln(writer,request.MultipartForm) log.Println(request.MultipartForm) }) }
FormValue&PostFormValue
The FormValue method will return the first value corresponding to the specified key in the form field without calling ParseForm and ParseMultipartForm
The PostFormValue method can only read PostForm
Both methods call ParseMultipartForm
When the enctype of your form is set to multipart / form data, the above two methods cannot obtain the form data
Upload file
First, the enctype type in the form should be set to multipart / form data
When processing uploaded files in GO language:
1. Call ParseMultiparForm method
2. Get fileHeader from the file field and call the Open method to get the file
3. You can use the ioutil.ReadAll function to read the file contents into the byte slice
func getFile(){ http.HandleFunc("/process", func(writer http.ResponseWriter, request *http.Request) { request.ParseMultipartForm(1024) //Because file is a map, that is, multiple files are allowed to be uploaded. You can specify to obtain that file here. 0 represents the first file fileHeader := request.MultipartForm.File["uploaded"][0] file,err := fileHeader.Open() if err == nil { data,err := ioutil.ReadAll(file) if err != nil { fmt.Println(err) } fmt.Fprintln(writer,string(data)) } }) }
Method 2
func getFile2(){ http.HandleFunc("/process", func(writer http.ResponseWriter, request *http.Request) { //Return to the first file. This method is faster when only one file is uploaded file,_,err := request.FormFile("uploaded") if err == nil { data,err := ioutil.ReadAll(file) if err != nil { fmt.Println(err) } fmt.Fprintln(writer,string(data)) } }) }
MultipartReader()
The source code is as follows:
func (r *Request) MultipartReader() (*multipart.Reader, error) { if r.MultipartForm == multipartByReader { return nil, errors.New("http: MultipartReader called twice") } if r.MultipartForm != nil { return nil, errors.New("http: multipart handled by ParseMultipartForm") } r.MultipartForm = multipartByReader
If it is a multipart / form data or multipart mixed POST request, the multipart reader will return a MIME multipart reader, otherwise it will return an error and nil
In use, you can use MultipartReader instead of ParseMultipartForm to process the requested body as a stream. Instead of processing the entire form data at one time, it checks the values from the form and processes one at a time
POST request - JSON BODY
Not all post requests come from the form
In different client frameworks, post requests are encoded in different ways
For example, jQuery usually uses application/x-www-form-urlencoded
Australian is application/json, but the ParseForm method cannot handle application/json
ResponseWriter
The ResponseWriter is required to return the corresponding from the server to the client
The ResponseWriter is an interface that the handler uses to return the corresponding. The behind the scenes struct that really supports the ResponseWriter is a non exported http.response
Write to ResponseWriter
ResponseWriter actually implements a pointer when it is implemented at the bottom
In the ResponseWriter, the write method receives a byte slice as a parameter and writes it into the corresponding HTTP Body.
If the content type is not set in the header when the Write method is called, the first 512 bytes of data will be used to monitor the content type
func writeExample(w http.ResponseWriter,r *http.Request) { str := `<html><head><title>Go Web</title></head> <body><h1>Hello World</h1></body> </html>` w.Write([]byte(str)) }
WriteHeader
The WriteHeader method receives an integer type (HTTP status code) as a parameter and returns it as the status code of the HTTP response. If this method is not called, it will be called implicitly before calling the Write method for the first time
After the WriteHeader is called, it can still be written to the ResponseWriter, but the header cannot be modified
func writeHeader(w http.ResponseWriter,r *http.Request) { w.WriteHeader(501) fmt.Fprintln(w,"66666666666666") }
Header
The Header method returns the map of headers. It can be modified. The modified headers will be reflected in the HTTP response returned to the client
func headerExample(w http.ResponseWriter,r * http.Request) { //Request to redirect access w.Header().Set("location","http://www.baidu.com") //Status code of access request w.WriteHeader(302) }
Incoming json data
type Post struct { User string Threads []string } func jsonExample(w http.ResponseWriter , r *http.Request) { w.Header().Set("Content-Type","application/json") post := &Post{ User: "Zhang San", Threads: []string{"666","777","888"}, } json,_:= json2.Marshal(post) w.Write(json) }
Built in Response
1.NotFound function, wrapping a 404 status code and an additional information
2. The serverfile function provides a file from the file system and returns it to the requester
3. Servercontent function, which can return the contents of anything that implements the io.ReadSeeker interface to the requester. At the same time, it can also process the Range request (Range request). If only part of the content of the resource is requested, servercontent can respond in this way. ServeFile or io.Copy does not work
4. The redirect function tells the client to redirect to another URL
tp://www.baidu.com")
//Status code of access request
w.WriteHeader(302)
}
afferent json data ```go type Post struct { User string Threads []string } func jsonExample(w http.ResponseWriter , r *http.Request) { w.Header().Set("Content-Type","application/json") post := &Post{ User: "Zhang San", Threads: []string{"666","777","888"}, } json,_:= json2.Marshal(post) w.Write(json) }
Built in Response
1.NotFound function, wrapping a 404 status code and an additional information
2. The serverfile function provides a file from the file system and returns it to the requester
3. Servercontent function, which can return the contents of anything that implements the io.ReadSeeker interface to the requester. At the same time, it can also process the Range request (Range request). If only part of the content of the resource is requested, servercontent can respond in this way. ServeFile or io.Copy does not work
4. The redirect function tells the client to redirect to another URL