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 throughajax
Send 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-Type
This 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;
- support
json/xml/http
Content 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
action
Represents the URL requested when the form is submitted,method
Method of representing a request.If you are usingGET
Request, due toGET
Method has no request body and the parameters will be concatenated to the end of the URL;enctype
Specifies 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.
Form
field
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].
PostForm
field
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:
MultipartForm
field
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 head
Content-Type
To know what format it is; - through
r.Body
Read 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.