09 How to Set Headers on Requests and Responses Using Httprouter in Golang
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:
- Golang Installed: Make sure Go is installed on your system.
- New Project Setup: Create a new project with a basic file structure.
- Httprouter Library: Install the httprouter library with the following command:
1go get github.com/julienschmidt/httprouter
Step 1: Setting Up the Project Structure
Create the following project structure:
1project/
2├── main.go
3└── handlers/
4 └── headers.goStep 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:
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:
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:
1go run main.goAccess the endpoint via a browser or use curl:
1curl -i http://localhost:8080/set-response-headerThe 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:
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:
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:
1curl -i -H "X-Requested-By: ClientApp" http://localhost:8080/validate-request-headerIf the header is valid, the response will be:
1Header valid!If the header is missing, the response will show an error:
1X-Requested-By header missingStep 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:
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
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:
1go test ./handlersConclusion
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: