Go net/http Request

Understanding and utilizing the http.Request struct in Go's standard library.

Introduction

The http.Request struct represents an incoming HTTP request. It's the primary way your server receives information about a client's request. Understanding its fields and methods is crucial for building robust web applications with Go.

Struct Definition

The http.Request struct is defined in the net/http package. Here's a simplified view of its key fields:


type Request struct {
    Method           string
    URL              *url.URL
    Proto            string
    ProtoMajor, ProtoMinor int

    Header       Header
    Body         io.ReadCloser
    GetBody      func() (io.ReadCloser, error)

    ContentLength int64
    TransferEncoding []string
    Close bool

    Form      url.Values
    PostForm  url.Values
    MultipartForm *multipart.Form

    RemoteAddr string
    RequestURI string

    // ... other fields
}
                

Common Fields

Method

The HTTP method of the request, such as GET, POST, PUT, DELETE, etc. It's typically uppercase.

req.Method 

URL

A pointer to a url.URL struct, which contains the parsed components of the request URL (scheme, host, path, query parameters, etc.).

req.URL 

You can access components like:

  • req.URL.Path: The path component of the URL.
  • req.URL.RawQuery: The raw query string.
  • req.URL.Query(): Returns a url.Values map of parsed query parameters.

Proto

The HTTP protocol version used for the request (e.g., HTTP/1.1, HTTP/2.0).

req.Proto 

An http.Header type, which is a map of string keys to string slices. It represents the request headers.

req.Header 

Example: Accessing the User-Agent header:

userAgent := req.Header.Get("User-Agent") 

Body

An io.ReadCloser interface representing the request body. For methods like POST or PUT, this is where you'd read data sent by the client.

bodyBytes, err := io.ReadAll(req.Body) 

Important: The Body must be closed after reading to release resources.

defer req.Body.Close() 

Form

A url.Values map containing the parsed form data. This is populated after calling ParseForm(). It includes both URL query parameters and form values from the request body (for POST requests with application/x-www-form-urlencoded content type).

formValue := req.Form.Get("myParam") 

PostForm

A url.Values map specifically containing form values from the request body. This is also populated after calling ParseForm() and is useful when distinguishing between query parameters and body form data.

postFormValue := req.PostForm.Get("myField") 

RemoteAddr

The network address that sent the request, in the form "host:port".

clientIP := req.RemoteAddr 

Context

A context.Context value associated with the request. This is crucial for managing request-scoped values, cancellation signals, and deadlines.

ctx := req.Context() 

Common Methods

ParseForm()

This method parses the request body and URL values into the Form and PostForm maps. It's essential to call this if you need to access form data from POST requests or query parameters.

err := req.ParseForm()
if err != nil {
    // Handle error
}
// Now req.Form and req.PostForm are populated
                

ParseForm() also handles multipart/form-data requests, populating req.MultipartForm.

ParseMultipartForm()

A more specific method to parse multipart/form-data requests. It takes a maximum memory limit as an argument.

maxMemory := int64(10 << 20) // 10 MB
err := req.ParseMultipartForm(maxMemory)
if err != nil {
    // Handle error
}
// Access files and fields via req.MultipartForm
                

Reading the Body

The request body is an io.ReadCloser. You typically use io.ReadAll or read in chunks. Remember to always close the body.

defer req.Body.Close()
bodyBytes, err := io.ReadAll(req.Body)
if err != nil {
    // Handle error
}
bodyString := string(bodyBytes)
// Process bodyString
                

Getting Header Values

The Header.Get(key) method is the preferred way to retrieve a header value. It returns the first value associated with the key, or an empty string if the key is not present.

contentType := req.Header.Get("Content-Type") 

For multiple values or checking for presence, use req.Header.Values(key) and req.Header.Contains(key).

Example Usage

Here's a simple Go HTTP handler demonstrating how to access request details:


package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
	// Accessing request details
	method := r.Method
	path := r.URL.Path
	userAgent := r.Header.Get("User-Agent")
	clientIP := r.RemoteAddr

	fmt.Fprintf(w, "

Request Details

") fmt.Fprintf(w, "

Method: %s

", method) fmt.Fprintf(w, "

Path: %s

", path) fmt.Fprintf(w, "

User-Agent: %s

", userAgent) fmt.Fprintf(w, "

Remote Address: %s

", clientIP) // Reading the request body (for POST, PUT, etc.) if r.Body != nil { defer r.Body.Close() bodyBytes, err := io.ReadAll(r.Body) if err != nil { log.Printf("Error reading request body: %v", err) http.Error(w, "Error reading request body", http.StatusInternalServerError) return } if len(bodyBytes) > 0 { fmt.Fprintf(w, "

Request Body:

") fmt.Fprintf(w, "
%s
", string(bodyBytes)) } else { fmt.Fprintf(w, "

Request Body: (empty)

") } } else { fmt.Fprintf(w, "

Request Body: (none)

") } // Parsing form data (if content type is appropriate) err := r.ParseForm() if err != nil { log.Printf("Error parsing form: %v", err) // Continue processing if form parsing fails, but log the error } else { if len(r.Form) > 0 { fmt.Fprintf(w, "

Form Data

") for key, values := range r.Form { fmt.Fprintf(w, "

%s: %v

", key, values) } } } } func main() { http.HandleFunc("/ms/docs/net/http/request", handler) log.Println("Server starting on :8080...") log.Fatal(http.ListenAndServe(":8080", nil)) }