Golang (or Go) is a programming language that has gained popularity in recent years due to its speed and ease of use. One of the most common use cases for Go is building HTTP web servers. In this blog, we will cover everything you need to know about building an HTTP web server in Golang.
Before diving into the code, let’s first understand the basics of HTTP web servers.
What is an HTTP Server?
An HTTP server is a program that listens for incoming HTTP requests and returns an HTTP response. The HTTP requests contain information such as the HTTP method (GET, POST, etc.), the URL being requested, and any data being sent in the request body. The HTTP response contains information such as the status code (200 for success, 404 for not found, etc.) and any data being sent in the response body.
Golang Web Server Types
The Golang net/http package provides various types for building web servers.
1. Golang HTTP Handle (http.Handle)
The http.Handle
function registers a handler for the given pattern.
func Handle(pattern string, handler Handler)
The first argument to Go http.Handle
is a string that represents the URL pattern to match. Second argument is a value that implements the http.Handler
interface.
func main() {
http.Handle("/", &myHandler{})
http.ListenAndServe(":8080", nil)
}
type myHandler struct{}
func (h *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, World!")
}
In the above example, we register the myHandler
struct as a handler for the root URL (“/”) using the http.Handle
function. The myHandler
struct must implement the http.Handler
interface by defining the ServeHTTP
method.
We can also use a function that satisfies the http.HandlerFunc
type as the second argument to http.Handle
.
func main() {
http.Handle("/", http.HandlerFunc(handler))
http.ListenAndServe(":8080", nil)
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, World!")
}
In this code, we register the handler function as a handler for the root URL (“/”) using the http.Handle
function.
2. Go HTTP web server ResponseWriter (http.ResponseWriter)
The http.ResponseWriter interface is used to write the response to an HTTP request. It has methods for setting headers and writing data to the response.
type ResponseWriter interface {
Header() http.Header
Write([]byte) (int, error)
WriteHeader(int)
}
The Header()
method returns the header map that will be sent with the response. Write
method writes the data to the response.
The WriteHeader
method writes the status code to the response.
3. Golang HTTP web Server Request (http.Request)
The http.Request
struct represents an HTTP request. It has fields for the HTTP method, URL, headers, and body.
type Request struct {
Method string
URL *url.URL
Header http.Header
Body io.ReadCloser
// ...
}
The Method field is the HTTP method (GET, POST, etc.). URL field is a pointer to a url.URL
struct that represents the URL of the request. The “Header” field is a map of the request headers. The Body
field is a ReadCloser
that represents the request body.
4. Go HTTP ServeMux (http.ServeMux)
The http.ServeMux
struct is a multiplexer for HTTP requests. It routes requests to their corresponding handler functions based on the request path.
type ServeMux struct {
func NewServeMux() *ServeMux
func (mux *ServeMux) Handle(pattern string, handler Handler)
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string)
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request)
}
The http.ServeMux
struct has methods for registering handler functions for different URL paths.
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", handler)
mux.HandleFunc("/about", aboutHandler)
http.ListenAndServe(":8080", mux)
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Welcome to the homepage!")
}
func aboutHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "This is the about page.")
}
In this code, we create a new http.ServeMux
struct using the http.NewServeMux
function. We register handler functions for the root URL (“/”) and the “/about” path using the mux.HandleFunc
method.
Finally, we start the server using the http.ListenAndServe
function, passing the mux object as the second argument.
5. Go web server HandleFunc (http.HandlerFunc)
The “http.HandlerFunc” type is a convenience type that allows us to define a handler function that satisfies the “http.Handler” interface.
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
We can use the “http.HandlerFunc” type to convert any function that has the signature of a handler function to a type that satisfies the “http.Handler” interface.
func main() {
http.Handle("/", http.HandlerFunc(handler))
http.ListenAndServe(":8080", nil)
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, World!")
}
In this code, we register the “handler” function as a handler for the root URL (“/”) using the “http.Handle” function. We use the “http.HandlerFunc” type to convert the “handler” function to a type that satisfies the “http.Handler” interface.
6. Golang web Server Response (http.Response)
The “http.Response” struct represents an HTTP response. It has fields for the status code, headers, and body.
type Response struct {
Status string
StatusCode int
Proto string
Header http.Header
Body io.ReadCloser
// ...
}
The “Status” field is a string that represents the status of the response (e.g. “200 OK”, “404 Not Found”). The “StatusCode” field is an integer that represents the HTTP status code (e.g. 200, 404). The “Header” field is a map of the response headers. The “Body” field is a ReadCloser that represents the response body.
We can use the “http.Response” struct to create an HTTP response and write it to the “http.ResponseWriter”.
func handler(w http.ResponseWriter, r *http.Request) {
resp := &http.Response{
StatusCode: http.StatusOK,
Proto: "HTTP/1.1",
Header: make(http.Header),
Body: ioutil.NopCloser(bytes.NewBufferString("Hello, World!")),
}
resp.Header.Set("Content-Type", "text/plain")
resp.Write(w)
}
In the above code, we create an “http.Response” struct with a status code of 200, a “Content-Type” header of “text/plain”, and a body of “Hello, World!”. We then write the response to the “http.ResponseWriter” using the “resp.Write” method.
Difference between Handle and HandleFunc
The main differences between Handle
and HandleFunc
are:
- Handle takes an object that implements the Handler interface as an argument, while
HandleFunc
takes a function that takes aResponseWriter
and a Request object as arguments. - Handle requires a custom struct and its associated method to be defined and registered, while
HandleFunc
allows the handler function to be defined and registered in a single step. HandleFunc
automatically wraps the given function in a Handler object that implements the Handler interface, while Handle requires us to define a custom struct and its associated method that implements the Handler interface.
Difference between Handler and HandleFunc
The main difference between Golang HTTP Server Handler and HandleFunc is that Handler is an interface, and we need to implement its ServeHTTP method to create a custom handler.
HandleFunc, on the other hand, is a method that takes a function as an argument and creates a Handler object by wrapping the function.
HandleFunc is more convenient to use for simple handlers that don’t require a custom type, while Handler is more flexible and allows us to create complex handlers with custom types.
Creating an HTTP Web Server in Golang
To create an HTTP web server in Golang, we first need to import the net/http
package. This package provides all the necessary functionality for building an HTTP server.
import "net/http"
Go NewServeMux
Once we have imported the net/http
package, we can create an HTTP server using the http.NewServeMux()
function. This function returns a new ServeMux
instance, which is a request multiplexer for HTTP servers.
func main() {
mux := http.NewServeMux()
}
Next, we need to define the handler functions for each HTTP request. Handler functions are functions that are called when an HTTP request is received. They are responsible for generating the HTTP response.
func handlerFunc(w http.ResponseWriter, r *http.Request) {
// generate the HTTP response
}
The http.ResponseWriter
parameter is used to write the response data to the client, and the http.Request
parameter contains information about the incoming HTTP request.
Now that we have defined our handler function, we need to register it with the ServeMux
instance. This is done using the http.HandleFunc()
function.
mux.HandleFunc("/", handlerFunc)
The first parameter to http.HandleFunc()
is the URL path pattern that the handler function should be registered for. In this case, we are registering our handler function for the root URL path.
Go Web Server ListenAndServe
Finally, we can start the Golang HTTP server using the http.ListenAndServe()
function.
http.ListenAndServe(":8080", mux)
The first parameter to http.ListenAndServe()
is the address that the server should listen on. In this case, we are listening on port 8080
. The second parameter is the ServeMux
instance that contains all the registered handler functions.
Putting it all together:
package main
import (
"fmt"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
})
http.ListenAndServe(":8080", mux)
}
Run server:
go run server.go
This command compiles and runs the server.go
file. If everything is set up correctly, you should see the message “Hello, world!” printed on the screen.
Open your web browser and navigate to http://localhost:8080
. You should see the message “Hello, world!” displayed in your browser. Congratulations, you have just created your first HTTP server in Golang!
Handling Web Server Routes in Go
Now that we have a basic understanding of how to create an HTTP web server in Golang, let’s explore how to handle different routes. In HTTP, a route is a specific URL path that a client can request. For example, the route /about
might return information about the website or the company that runs it.
Let’s modify our code to handle two different routes: /
and /about
.
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", home)
http.HandleFunc("/about", about)
http.ListenAndServe(":8080", nil)
}
func home(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Welcome to the home page!")
}
func about(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "This is the about page.")
}
Here we have created two new functions: home
and about
. The home
function handles requests to the /
route, while the about
function handles requests to the /about
route. Each function simply writes a different message to the response.
Let’s run the server again and test out our new routes. In your terminal, run the following command:
go run main.go
Open your web browser and navigate to http://localhost:8080
and http://localhost:8080/about
. You should see the messages “Welcome to the home page!” and “This is the about page.” displayed in your browser, respectively.
Golang HTTP Headers
Setting HTTP headers is an important aspect of building an HTTP server. HTTP headers provide additional information about the request and response, such as content type, language, and encoding.
Some common HTTP headers are:
- Content-Type: Specifies the type of data in the request or response body.
- Content-Length: Specifies the length of the request or response body in bytes.
- User-Agent: Specifies the software used to send the request.
- Accept: Specifies the media types accepted by the client.
- Authorization: Specifies the authentication credentials for the request.
Setting Response Headers in Golang
To set response headers in Golang, we can use the “http.Header” type. The “http.Header” type is a map of string keys and string values that represent the headers. We can set headers using the “Set” method or add headers using the “Add” method.
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
func handler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
fmt.Fprint(w, "Hello, World!")
}
In this code, we set the “Content-Type” header to “text/plain” using the “w.Header().Set” method. We then write the response using the “fmt.Fprint” function.
Setting Request Headers in Golang
To set request headers in Golang, we can use the http.NewRequest
function. The http.NewRequest
function creates a new request with the specified method, URL, and body. We can set headers using the “Header” field of the request.
func main() {
req, err := http.NewRequest("GET", "https://example.com", nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("User-Agent", "My-User-Agent")
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
// handle response
}
In this code, we create a new request using the http.NewRequest
function with the GET method, the “https://example.com” URL, and a nil body. We set the “User-Agent” header using the Header.Set
method. We then make the request using the http.DefaultClient.Do
function and handle the response.
Delete HTTP Headers in Golang
To delete a header from an HTTP request, we can use the “http.Request.Header.Del” method. This method takes a string argument that specifies the name of the header to be deleted.
func handler(w http.ResponseWriter, r *http.Request) {
r.Header.Del("Authorization")
// ...
}
In this example, we delete the “Authorization” header from the incoming HTTP request using the http.Request.Header.Del
method.
Similarly, to delete a header from an HTTP response, we can use the http.ResponseWriter.Header().Del
method. This method takes a string argument that specifies the name of the header to be deleted.
func handler(w http.ResponseWriter, r *http.Request) {
w.Header().Del("Any-Option")
// ...
}
In this example, we delete the “Any-Option” header from the outgoing HTTP response using the “http.ResponseWriter.Header().Del” method.
Go Web Server HTTP Status Codes
HTTP status codes are three-digit numbers returned by a server to indicate the status of a response to a client’s request.
In Golang, the “net/http” package provides constants for all the HTTP status codes. Here are the five classes of HTTP status codes and their corresponding Golang constants:
1xx (Informational): These status codes indicate that the server has received the request and is continuing to process it.
- http.StatusContinue (100)
- http.StatusSwitchingProtocols (101)
2xx (Success): These status codes indicate that the request was successfully received, understood, and accepted.
- http.StatusOK (200)
- http.StatusCreated (201)
- http.StatusAccepted (202)
- http.StatusNonAuthoritativeInfo (203)
- http.StatusNoContent (204)
- http.StatusResetContent (205)
- http.StatusPartialContent (206)
3xx (Redirection): These status codes indicate that the client must take additional action to complete the request.
- http.StatusMultipleChoices (300)
- http.StatusMovedPermanently (301)
- http.StatusFound (302)
- http.StatusSeeOther (303)
- http.StatusNotModified (304)
- http.StatusUseProxy (305)
- http.StatusTemporaryRedirect (307)
- http.StatusPermanentRedirect (308)
4xx (Client Error): These status codes indicate that the client has made a bad request.
- http.StatusBadRequest (400)
- http.StatusUnauthorized (401)
- http.StatusPaymentRequired (402)
- http.StatusForbidden (403)
- http.StatusNotFound (404)
- http.StatusMethodNotAllowed (405)
- http.StatusNotAcceptable (406)
- http.StatusProxyAuthRequired (407)
- http.StatusRequestTimeout (408)
- http.StatusConflict (409)
- http.StatusGone (410)
- http.StatusLengthRequired (411)
- http.StatusPreconditionFailed (412)
- http.StatusRequestEntityTooLarge (413)
- http.StatusRequestURITooLong (414)
- http.StatusUnsupportedMediaType (415)
- http.StatusRequestedRangeNotSatisfiable (416)
- http.StatusExpectationFailed (417)
- http.StatusTeapot (418)
- http.StatusUnprocessableEntity (422)
- http.StatusLocked (423)
- http.StatusFailedDependency (424)
- http.StatusUpgradeRequired (426)
- http.StatusPreconditionRequired (428)
- http.StatusTooManyRequests (429)
- http.StatusRequestHeaderFieldsTooLarge (431)
- http.StatusUnavailableForLegalReasons (451)
5xx (Server Error): These status codes indicate that the server has encountered an error while processing the request.
- http.StatusInternalServerError (500)
- http.StatusNotImplemented (501)
- http.StatusBadGateway (502)
- http.StatusServiceUnavailable (503)
- http.StatusGatewayTimeout (504)
- http.StatusHTTPVersionNotSupported (505)
- http.StatusVariantAlsoNegotiates (506)
- http.StatusInsufficientStorage (507)
- http.StatusLoopDetected (508)
- http.StatusNotExtended (510)
- http.StatusNetworkAuthenticationRequired (511)
When writing When writing HTTP servers in Golang, it is important to understand how to use these status codes appropriately.
In Golang, we can set and delete HTTP status codes using the “net/http” package.
Setting HTTP Status Code in Go
To set an HTTP status code in Golang, we need to use the “http.ResponseWriter” interface. The interface provides a “WriteHeader” function that takes an HTTP status code as an argument.
func handler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "OK")
}
In this code, we set the HTTP status code to 200 (OK) using the “WriteHeader” function of the ResponseWriter interface. We then write the response using the “fmt.Fprint” function.
Deleting HTTP Status Code in Golang
To delete an HTTP status code in Golang, we need to use the “http.ResponseWriter” interface. The interface provides a “Flush” function that sends any buffered data to the client and resets the ResponseWriter to its initial state.
func handler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "OK")
w.(http.Flusher).Flush()
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "OK")
}
In this example code, we set the HTTP status code to 200 (OK) using the “WriteHeader” function of the ResponseWriter interface. We then write the response using the “fmt.Fprint” function. Then call the “Flush” function of the ResponseWriter interface to send the buffered data to the client and reset the ResponseWriter to its initial state. We then set the HTTP status code to 200 (OK) again using the “WriteHeader” function of the ResponseWriter interface and write the response again.
Best Practices of Web Server in Go
some best practices that should be followed when building a Golang HTTP web server.
Secure Your Web Server with TLS
When dealing with sensitive data, it is always recommended to use Transport Layer Security (TLS). TLS provides encryption and authentication, making it difficult for attackers to intercept and tamper with data being transmitted. In Golang, TLS can be easily implemented using the “crypto/tls” package.
server := &http.Server{
Addr: ":443",
Handler: handler,
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{cert},
},
}
log.Fatal(server.ListenAndServeTLS("", ""))
In this code, we create an HTTP server that listens on port 443 and uses TLS for encryption and authentication. The server’s TLS configuration is defined using the “crypto/tls” package, and a TLS certificate is specified.
Better Resource Management with Context
The “context” package in Golang provides a way to carry deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes. Using context in HTTP requests allows for better management of resources, cancellation of long-running requests, and more.
func handler(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), time.Second)
defer cancel()
// Use ctx in the code logic
}
In this code, we create a new context using the “context.WithTimeout” function and pass it the request’s context and a timeout of one second. We then use the context in the code logic and defer the cancellation of the context.
Separation of Concerns with Middleware
Middleware is a way to add functionality to an HTTP server without modifying the server’s core logic. It allows for better separation of concerns and can help reduce code duplication. In Golang, middleware can be implemented using the “net/http” package’s “HandlerFunc” function.
func logger(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %s", r.Method, r.URL.Path)
next.ServeHTTP(w, r)
})
}
func handler(w http.ResponseWriter, r *http.Request) {
// handler code
}
func main() {
handler := http.HandlerFunc(handler)
http.Handle("/", logger(handler))
http.ListenAndServe(":8080", nil)
}
In this code, we define a “logger” middleware function that logs the HTTP method and URL path of incoming requests. We then wrap our main “handler” function with the “logger” middleware function using the “http.HandlerFunc” function. Finally, we pass the wrapped handler to the “http.Handle” function and listen on port 8080.
If you want to learn more about Golang and web development, check out the following resources:
- The official Golang documentation: https://golang.org/doc/
- The
net/http
package documentation: https://golang.org/pkg/net/http/