Skip to content
Santekno.com | Level Up Your Engineering Skills
EN
📖 0%
19 Jan 2025 · 5 min read ·Article 68 / 119
Go

09 How to Set Headers on Requests and Responses Using Httprouter in Golang

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

In web development, managing HTTP request and response headers is crucial for handling data, security, and metadata. This article provides a step-by-step guide on setting headers using the httprouter library in Golang. With this guide, even beginners can grasp the concept with ease.


Prerequisites

Before getting started, ensure you have the following:

  1. Golang Installed: Make sure Go is installed on your system.
  2. New Project Setup: Create a new project with a basic file structure.
  3. Httprouter Library: Install the httprouter library with the following command:
    bash
    1go get github.com/julienschmidt/httprouter

Step 1: Setting Up the Project Structure

Create the following project structure:

bash
1project/
2├── main.go
3└── handlers/
4    └── headers.go

Step 2: Setting Headers in Responses

Response headers are sent to the client via the http.ResponseWriter object. Let’s add a handler to set response headers.

1. Creating the Handlers File

Open handlers/headers.go and add the following code:

go
 1package handlers
 2
 3import (
 4	"net/http"
 5	"github.com/julienschmidt/httprouter"
 6)
 7
 8// ResponseHeaderHandler adds headers to the response
 9func ResponseHeaderHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
10	w.Header().Set("Content-Type", "application/json")
11	w.Header().Set("X-Custom-Header", "Learning Golang")
12	w.WriteHeader(http.StatusOK)
13	w.Write([]byte(`{"message":"Headers set successfully!"}`))
14}

2. Connecting the Handler in main.go

Open main.go and add the following handler:

go
 1package main
 2
 3import (
 4	"log"
 5	"net/http"
 6	"github.com/julienschmidt/httprouter"
 7	"project/handlers"
 8)
 9
10func main() {
11	router := httprouter.New()
12
13	// Add the response header handler
14	router.GET("/set-response-header", handlers.ResponseHeaderHandler)
15
16	log.Println("Server running at http://localhost:8080")
17	log.Fatal(http.ListenAndServe(":8080", router))
18}

3. Testing the Endpoint

Run the server:

bash
1go run main.go

Access the endpoint via a browser or use curl:

bash
1curl -i http://localhost:8080/set-response-header

The response will display the headers that have been set.


Step 3: Setting Headers in Requests

To manipulate request headers, we can use the http.Request object. Here, we’ll create middleware that reads and processes request headers.

1. Adding Middleware

Open handlers/headers.go again and add the following function:

go
 1// RequestHeaderHandler processes headers from the request
 2func RequestHeaderHandler(next httprouter.Handle) httprouter.Handle {
 3	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
 4		headerValue := r.Header.Get("X-Requested-By")
 5		if headerValue == "" {
 6			http.Error(w, "X-Requested-By header missing", http.StatusBadRequest)
 7			return
 8		}
 9
10		next(w, r, ps)
11	}
12}

2. Adding Middleware to main.go

Modify main.go to use the middleware:

go
1router.GET("/validate-request-header", handlers.RequestHeaderHandler(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
2	w.Write([]byte("Header valid!"))
3}))

3. Testing the Endpoint

Run the server again and use curl:

bash
1curl -i -H "X-Requested-By: ClientApp" http://localhost:8080/validate-request-header

If the header is valid, the response will be:

text
1Header valid!

If the header is missing, the response will show an error:

text
1X-Requested-By header missing

Step 4: Writing Unit Tests

Writing unit tests ensures our functions work as expected.

1. Testing ResponseHeaderHandler

Create handlers/headers_test.go and add the following test:

go
 1package handlers
 2
 3import (
 4	"net/http"
 5	"net/http/httptest"
 6	"testing"
 7	"github.com/julienschmidt/httprouter"
 8)
 9
10func TestResponseHeaderHandler(t *testing.T) {
11	type args struct {
12		image string
13	}
14	tests := []struct {
15		name              string
16		args              args
17		wantHTTPStatus    int
18		wantContentType   string
19		wantXCustomHeader string
20		wantResponse      string
21	}{
22		{
23			name: "test get image",
24			args: args{
25				image: "photo.jpg",
26			},
27			wantHTTPStatus:    200,
28			wantContentType:   "application/json",
29			wantXCustomHeader: "Belajar Golang",
30			wantResponse:      `{"message":"Header berhasil disetel!"}`,
31		},
32	}
33	for _, tt := range tests {
34		t.Run(tt.name, func(t *testing.T) {
35			request := httptest.NewRequest(http.MethodGet, "http:localhost:8080/set-response-header", nil)
36			recorder := httptest.NewRecorder()
37			ResponseHeaderHandler(recorder, request, httprouter.Params{})
38
39			// checl status code
40			if status := recorder.Code; status != tt.wantHTTPStatus {
41				t.Errorf("Response code is %v, want %v", status, tt.wantHTTPStatus)
42			}
43
44			// check content type is application/json
45			if contentType := recorder.Header().Get("Content-Type"); contentType != tt.wantContentType {
46				t.Errorf("Content-Type header is %v, want %v", contentType, tt.wantContentType)
47			}
48
49			// check header x-custom-header return
50			if customHeader := recorder.Header().Get("X-Custom-Header"); customHeader != tt.wantXCustomHeader {
51				t.Errorf("X-Custom-Header is %v, want %v", customHeader, tt.wantXCustomHeader)
52			}
53
54			// check body response
55			if recorder.Body.String() != tt.wantResponse {
56				t.Errorf("Body is %v, want %v", recorder.Body.String(), tt.wantResponse)
57			}
58		})
59	}
60}

2. Testing RequestHeaderHandler

go
 1func TestRequestHeaderHandler(t *testing.T) {
 2	type args struct {
 3		XRequestBy string
 4	}
 5	tests := []struct {
 6		name         string
 7		args         args
 8		wantResponse string
 9		wantStatus   int
10	}{
11		{
12			name: "valid request header",
13			args: args{
14				XRequestBy: "ClientApp",
15			},
16			wantResponse: "header valid",
17			wantStatus:   http.StatusOK,
18		},
19		{
20			name: "invalid request header",
21			args: args{
22				XRequestBy: "",
23			},
24			wantResponse: fmt.Sprintln("X-Requested-By header missing"),
25			wantStatus:   http.StatusBadRequest,
26		},
27	}
28	for _, tt := range tests {
29		t.Run(tt.name, func(t *testing.T) {
30			// create request with valid header
31			req, err := http.NewRequest("GET", "/validate-request-header", nil)
32			if err != nil {
33				t.Fatal(err)
34			}
35			req.Header.Set("X-Requested-By", tt.args.XRequestBy)
36
37			// cxreate response recorder to catch response
38			rr := httptest.NewRecorder()
39
40			// calling middleware and handler
41			handler := RequestHeaderHandler(ValidHeader)
42			handler(rr, req, httprouter.Params{})
43
44			// check when status code will expectation is 400 bad request
45			if status := rr.Code; status != tt.wantStatus {
46				t.Errorf("Response code is %v, want %v", status, tt.wantStatus)
47			}
48
49			// check when response body with expectation
50			if rr.Body.String() != tt.wantResponse {
51				t.Errorf("Body is %s, want %s", rr.Body.String(), tt.wantResponse)
52			}
53		})
54	}
55}
56
57func ValidHeader(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
58	w.Write([]byte("header valid"))
59}

Run the tests using:

bash
1go test ./handlers

Conclusion

With httprouter, you can easily set headers on HTTP requests and responses. Managing headers is crucial for client-server communication, including security enforcement, caching control, and metadata handling.

For those looking to dive deeper, explore concepts like middleware chaining to manage multiple logic flows modularly. Additionally, consider using debugging tools like Postman or Insomnia to visually test headers, or add logging to your middleware to track headers during development.

We hope this guide helps you better understand header management in Golang web development. If you have any questions, feel free to discuss!

You can also explore related articles:

Related Articles

💬 Comments