Create Middleware
http.Handler
wrapper is a function that has one input argument and one output argument, both of type http.Handler.
Example:
func Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//code before make the backend api call
next.ServeHTTP(w, r) //serve and handle the http request
//code after make the backend api call
})
}
Now, instead of directly serving and listening our http handler like:
http.ListenAndServe(":3000", handler)
We can now wrap it within our middleware and becomes:
http.ListenAndServe(":3000", Middleware(handler))
Prerequisites:
- Please make sure you already under the basic of REST API in Go.
- Please make sure you are already understand how to create a simple web server with GO.
Before everything started:
- I will be using back the previous API example for testing our middleware.
- I also created a simple webpage and host in PORT 5500 by using Live Server so that we can make the http request from different origin.
Client side code (Front end)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<label>Name :</label>
<input type="text" id="name" />
</div>
<div>
<label>Age :</label>
<input type="number" id="age" />
</div>
<div>
<label>Occupation :</label>
<input type="text" id="occupation" />
</div>
<button type="submit" onclick="CreateUser()">Submit</button>
<button type="submit" onclick="GetAllUser()">Get</button>
</body>
<script>
async function CreateUser(){
const name = document.getElementById("name");
const age = document.getElementById("age");
const occupation = document.getElementById("occupation");
const user = {};
user.name = name;
user.age = age;
user.occupation = occupation;
const response = await fetch("http://localhost:3000/createUser",{
method: "POST",
header: new Headers({
"Content-Type": "application/json"
}),
mode: "cors",
body: JSON.stringify(user)
})
const result = await response.json()
console.log("result",result);
}
</script>
</html>
Middleware code
func Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
referer := r.Referer() //return "http://127.0.0.1:5500/"
origin := r.Header.Get("origin") //return "http://127.0.0.1:5500"
requestUrl := r.URL.Path //return "/createUser"
method := r.Method //return "POST"
//In case if only allow POST and GET request
if method != "POST" && method != "GET"{
//Response bad request if doesn't pass the condition
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Bad Request"))
return
}
next.ServeHTTP(w, r)
fmt.Println("after api call")
})
}
Explanation
- Client side make http request
- http.Handler will go through the middleware function before heading to the route function
- We can check and verify many things before we move on to the route function such as
- Check the http request method
- Verify the origin and referer. The referer will follow whatever from the front-end url (including routes and query parameters) if the Referrer-Policy is strict-origin-when-cross-origin
- Security check such as getting the JWT token from Authorization header
r.Header.Get("Authorization")
and verify the token.
- After it passes all conditions, we can now serve the actual API route function by calling
next.ServeHTTP(w, r)
- In case we might want to execute another actions after our API is successfully response to the client side.
- It’s quite easy in Go as we just need to add our code right after the
next.ServeHTTP(w, r)
function.