GO WBE learning notes

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

Tags: Go Back-end

Posted on Tue, 09 Nov 2021 20:27:54 -0500 by danielle