An overview of the

Earlier we learned about processors and processor functions, and how to write and register processors. In this article we’ll learn how to get information from a request.

Structure of request

From what we learned earlier, we know that processor functions need to comply with the following signature:

func (w http.ResponseWriter, r *http.Request)
Copy the code

HTTP.Request is the type of Request. Any data passed by the client can be retrieved from this structure. The structure Request is defined in the package NET/HTTP:

// src/net/http/request.go

type Request struct {
    Method          string    
    URL             *url.URL
    Proto           string
    ProtoMajor      int
    ProtoMinor      int
    Header          Header
    Body            io.ReadCloser
    ContentLength   int
    // omit some fields...
}
Copy the code

Let’s look at a few important fields.

Method

The Method field in the request indicates which Method of the server the client wants to invoke. In the first article, we mentioned the HTTP protocol method. The value can be GET, POST, PUT, or DELETE. The server will conduct different processing according to different request methods, such as GET method only to obtain information (basic user information, commodity information, etc.), POST method to create new resources (registration of new users, new products, etc.).

URL

When Tim Berners-Lee created the World Wide Web, he also introduced the concept of using strings to represent Internet resources. He calls this string a Uniform Resource Identifier (URI). A URI consists of two parts. One section represents the Name of the Resource, which is the Uniform Resource Name (URN). The other part represents the Location of the Resource, namely the Uniform Resource Location (URL).

In HTTP requests, urls are used to describe the location of the resource to be manipulated. The general format of the URL is:

[scheme:][//[userinfo@]host][/]path[?query][#fragment]
Copy the code
  • scheme: Indicates the protocol namehttphttpsftp;
  • userInfo: If yes, it indicates the user information, such as the user name and passworddj:password;
  • host: indicates the host domain name or address and optional port information. If the port number is not specified, the default value is 80. For example,www.example.com.www.example.com:8080.127.0.0.1:8080;
  • path: Indicates the path of the resource on the host/Space, such as/posts;
  • query: An optional query string, used by the client as a key-value pair argument=, between multiple key-value pairs&Connection, such aspage=1&count=10;
  • fragment: fragment, also called anchor point. Represents location information on a page. This information is usually not present in the URL of the request initiated by the browser. But you can get throughajaxSend this data in such a way as code;

Let’s look at a full URL:

http://dj:[email protected]/posts?page=1&count=10#fmt
Copy the code

The URL structure in Go is defined in the NET/URL package:

// net/url/url.go
type URL struct {
    Scheme      string
    Opaque      string
    User        *Userinfo
    Host        string
    Path        string
    RawPath     string
    RawQuery    string
    Fragment    string
}
Copy the code

You can obtain this information through the URL field in the request object. Next, let’s write a program to see in detail (using the basic structure of a Web program described in the previous article, just adding processor functions and registrations) :

func urlHandler(w http.ResponseWriter, r *http.Request) {
    URL := r.URL
    
    fmt.Fprintf(w, "Scheme: %s\n", URL.Scheme)
    fmt.Fprintf(w, "Host: %s\n", URL.Host)
    fmt.Fprintf(w, "Path: %s\n", URL.Path)
    fmt.Fprintf(w, "RawPath: %s\n", URL.RawPath)
    fmt.Fprintf(w, "RawQuery: %s\n", URL.RawQuery)
    fmt.Fprintf(w, "Fragment: %s\n", URL.Fragment)
}

/ / register
mux.HandleFunc("/url", urlHandler)
Copy the code

Localhost :8080/url/posts? Page = 1 & count = 10 # main:

Scheme: 
Host: 
Path: /url/posts
RawPath: 
RawQuery: page=1&count=10
Fragment:
Copy the code

Why do empty fields appear? Notice a comment in the URL field of the source Request structure:

// URL specifies either the URI being requested (for server
// requests) or the URL to access (for client requests).
//
// For server requests, the URL is parsed from the URI
// supplied on the Request-Line as stored in RequestURI.  For
// most requests, fields other than Path and RawQuery will be
// empty. (See RFC 7230, Section 5.3)
//
// For client requests, the URL's Host specifies the server to
// connect to, while the Request's Host field optionally
// specifies the Host header value to send in the HTTP
// request.
Copy the code

When a request is received as a server, the URL is empty except for Path and RawQuery. This Issue has been discussed at Issue 28940 on Go’s Github repository.

We can also get a URL string from the URL structure:

URL := &net.URL {
    Scheme:     "http",
    Host:       "example.com",
    Path:       "/posts",
    RawQuery:   "page=1&count=10",
    Fragment:   "main",
}
fmt.Println(URL.String())
Copy the code

The above program runs the output string:

http://example.com/posts?page=1&count=10#main
Copy the code

Proto/ProtoMajor/ProtoMinor

Proto indicates the HTTP version, for example, HTTP/1.1. ProtoMajor indicates the major version, and ProtoMinor indicates the minor version.

func protoFunc(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Proto: %s\n", r.Proto)
    fmt.Fprintf(w, "ProtoMajor: %d\n", r.ProtoMajor)
    fmt.Fprintf(w, "ProtoMinor: %d\n", r.ProtoMinor)
}

mux.HandleFunc("/proto", protoFunc)
Copy the code

Localhost :8080

Proto: HTTP/1.1
ProtoMajor: 1
ProtoMinor: 1
Copy the code

HTTP/1.1 is the current mainstream version.

Header

Header Stores the Header information sent by the client in key-value pairs. Map [string][]string:

// src/net/http/header.go
type Header map[string] []string
Copy the code

The keys and values in each header are strings, and multiple identical keys can be set. Notice that the Header value is []string, which holds multiple values for the same key. When a browser makes an HTTP request, it automatically adds headers. Let’s write a program to see:

func headerHandler(w http.ResponseWriter, r *http.Request) {
    for key, value := range r.Header {
        fmt.Fprintf(w, "%s: %v\n", key, value)
    }
}

mux.HandleFunc("/header", headerHandler)
Copy the code

Localhost :8080/header:

Accept-Encoding: [gzip, deflate, br] Sec-Fetch-Site: [none] Sec-Fetch-Mode: [navigate] Connection: [keep-alive] upset-insecure -Requests: [1] User-agent: [Mozilla/5.0 (Windows NT 10.0; W)in64; X64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36] sec-fetch -User: [?1] Accept: [text/HTML, application/XHTML + XML, application/XML; q = 0.9, image/webp image/apng, * / *; q = 0.8, application/signed - exchange; V = b3] Accept - Language: [useful - CN, useful; q = 0.9, en - US; q = 0.8, en; q = 0.7]Copy the code

I use Chrome, and different browsers add different headers.

Common headers are:

  • Accept: The type of content the client wants the server to send;
  • Accept-Charset: indicates the character encoding that the client can accept.
  • Content-Length: of the request principalbyteLength, usually in POST/PUT requests.
  • Content-TypeThis header is used to record the type of the body content when the request body is included. When sending a POST or PUT request, the content type defaults tox-www-form-urlecoded. However, when uploading files, the type should be set tomultipart/form-data.
  • User-Agent: Used to describe the client that initiated the request, such as what browser.

Content-Length/Body

Content-length specifies the Length of the request Body in bytes. The request Body can be read from the Body field. Careful friends may have noticed that the Body field is an IO.ReadCloser interface. Close it after reading, otherwise there will be resource leaks. You can use defer to simplify code writing.

func bodyHandler(w http.ResponseWriter, r *http.Request) {
    data := make([]byte, r.ContentLength)
    r.Body.Read(data) // Ignore error handling
    defer r.Body.Close()
    
    fmt.Fprintln(w, string(data))
}

mux.HandleFunc("/body", bodyHandler)
Copy the code

The above code passes the request body content from the client back to the client. You can also use the IO /ioutil package to simplify reading operations:

data, _ := ioutil.ReadAll(r.Body)
Copy the code

Entering the URL directly in the browser initiates a GET request, which does not carry the request body. There are many ways to initiate a request with a request body. Here are two:

Use the form

With HTML forms we can send a POST request to the server, sending the content of the form as the request body.

func indexHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, '< HTML >  Go Web programming request   
      
) } mux.HandleFunc("/", indexHandler) Copy the code

Use a form in HTML to display a form. When the submit button is clicked, the browser sends a POST request to the path /body with the user name and mailbox as the body of the request package.

Start the server, go to the home page localhost:8080/, and display the form. Fill in the information and click Submit:

The browser sends a POST request to the server with the URL /body. The bodyHandler then sends the body of the packet back to the client. Finally, the client displays:

The data above uses X-www-form-urlencoded encoding, which is the default encoding for forms. More on this later.

Use the Postman

Postman is a very powerful API testing tool.

  • All method requests that support HTTP protocol (GET/POST/PUT/DELETE).
  • Can carry header information in the request, the content of the request body;
  • supportjson/xml/httpContent in various formats;
  • Friendly interface.

Let’s take a look at how to test our bodyHandler using PostMan.

  • Black: Select HTTP protocol method, here select POST so that the request body can be carried;
  • Green: requested URL;
  • Blue: can set the request header, request body;
  • Light red: The request body supports multiple formats, so select the original format.
  • Gray: the specific content of the request body;
  • Red part: response information displayed after sending, you can view the response header, Cookie, response body, etc. You can see that it is returned as is.

Get request parameters

Above we analyzed the common fields of HTTP requests in Go. In real development, the client usually needs to pass some parameters in the request. Parameters can be passed in two ways:

  • A key/value pair in a URL, also called a query string;
  • The form.

The following are introduced in turn.

URL key-value pairs

As mentioned earlier in the general format of the URL, the URL can be followed by an optional query string, starting with? It is separated from a path, for example, key1=value1&key2=value2.

There is a RawQuery field in the URL structure. This field is the query string.

func queryHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, r.URL.RawQuery)
}

mux.HandleFunc("/query", queryHandler)
Copy the code

If we call localhost:8080/query? Name =dj&age=20 request, the query string name=dj&age=20 is returned to the client as is. RawQuery is a string, so string parsing can be used as well, but it’s too cumbersome!!

The form

In a narrow sense, a form sends a request through a form. In a broad sense, data can be sent to the server in a request body. Let’s simply write an HTML page and send an HTTP request via the page form:

<html>
    <head>
        <title>Go Web programming request</title>
    </head>
    
    <body>
        <form action="/form? lang=cpp&name=dj" method="post" enctype="application/x-www-form-urlencoded">
            <label>Form:</label>
            <input type="text" name="lang" />
            <input type="text" name="age" />
            <button type="submit">submit</button>
        </form>
    </body>
</html>
Copy the code
  • actionRepresents the URL requested when the form is submitted,methodMethod of representing a request.If you are usingGETRequest, due toGETMethod has no request body and the parameters will be concatenated to the end of the URL;
  • enctypeSpecifies how to encode the request body. Default isapplication/x-www-form-urlencoded. To send files, you must specify asmultipart/form-data;

Let’s see what urlencoded is. RFC 3986 defines reserved and non-reserved words in URLS. All reserved characters need TO be encoded in URLS. The URL encoding converts the character to its ASCII equivalent byte value, which is then represented as a two-digit hexadecimal number, preceded by a percent sign (%). For example, the ASCII code of the space is 32 and the hexadecimal code is 20, so the URL code is %20.

Formfield

Use x-www-form-urlencoded request body, first call request ParseForm method parsing, then fetch data from form field:

func formHandler(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    fmt.Fprintln(w, r.Form)
}

mux.HandleFunc("/form", formHandler)
Copy the code

Run the program and verify the results:

The Form field type url.Values is actually map[string][]string. After calling the ParseForm method, you can manipulate the data using the urL.values method.

Using ParseForm, you can also parse the query string by changing the form above to:

<html>
    <head>
        <title>Go Web programming request</title>
    </head>
    
    <body>
        <form action="/form? lang=cpp&name=dj" method="post" enctype="application/x-www-form-urlencoded">
            <label>Form:</label>
            <input type="text" name="lang" />
            <input type="text" name="age" />
            <button type="submit">submit</button>
        </form>
    </body>
</html>
Copy the code

Request result:

As you can see, the key-value pairs in the query string are merged with the parsing processing in the form. Under the same key, the form value always comes first, as in [golang CPP].

PostFormfield

If a request has both URL key-value pairs and form data, and the user only wants the form data, the PostForm field can be used.

Using PostForm returns only form data, not URL key values. If you change r.column to R.postform in the above program, the program displays the following result:

MultipartFormfield

If you want to process uploaded files, you must use multipart/form-data encoding. Similar to the previous Form/PostForm, multipart/form-data encoding requests need to be parsed before being used. It just uses a different method, ParseMultipartForm, and then values from the MultipartForm field.

<form action="/multipartform? lang=cpp&name=dj" method="post" enctype="multipart/form-data">
	<label>MultipartForm:</label>
    <input type="text" name="lang" />
    <input type="text" name="age" />
    <input type="file" name="uploaded" />
    <button type="submit">submit</button>
</form>
Copy the code
func multipartFormHandler(w http.ResponseWriter, r *http.Request) {
    r.ParseMultipartForm(1024)
    fmt.Fprintln(w, r.MultipartForm)
    
    fileHeader := r.MultipartForm.File["uploaded"] [0]
	file, err := fileHeader.Open()
	iferr ! =nil {
		fmt.Println("Open failed: ", err)
		return
	}

	data, err := ioutil.ReadAll(file)
	if err == nil {
		fmt.Fprintln(w, string(data))
	}
}

mux.HandleFunc("/multipartform", multipartFormHandler)
Copy the code

Run the program:

MultipartForm contains two map-type fields, one for form key-value pairs and one for uploaded file information.

Get multipartForm. File with the name of the File control in the form to get the files uploaded through the control. The result is the multipart.FileHeader type, which lets you get the various attributes of the file.

Note that this method is used to process files. For security purposes, the ParseMultipartForm method needs to pass a parameter indicating the maximum memory usage to avoid uploading files that take up too much space.

FormValue/PostFormValue

To get values easily, the NET/HTTP package provides FormValue/PostFormValue methods. They are on the need to automatically call ParseForm/ParseMultipartForm method.

The FormValue method returns the value of the specified key in the requested Form field. If there are multiple values for the same key, the first value is returned. If you need to get all the values, use the Form field directly. The following code returns the first value of hello:

fmt.Fprintln(w, r.FormValue("hello"))
Copy the code

The PostFormValue method returns the value of the specified key in the PostForm field of the request. If there are multiple values for the same key, the first value is returned. If you need to get all the values, use the PostForm field directly

Note: When encoding is specified as multipart/form-data, FormValue/PostFormValue will not return any value, they will read form /PostForm fields, ParseMultipartForm writes data to the MultipartForm field.

Other formats

Using techniques such as AJAX, you can send data in other formats, such as Application/JSON, etc. In this case:

  • First through the headContent-TypeTo know what format it is;
  • throughr.BodyRead byte stream;
  • Decoder use.

conclusion

This article covered various aspects of a request in a NET/HTTP package. From the Request structure to how parameters are passed, and finally how various encoded requests are handled.