This page looks best with JavaScript enabled

Building Web Server with Go - Part 1

 ·  ☕ 5 min read
This is part 1 of 09 part series on building web server with go.
Checkout https://www.gophersumit.com/series/web-server/ for more.

net/http package

net/http package comes as part of standard library of Go. This provides HTTP client and server implementations. For building web servers with Go, there is no external library required.

package documentation https://golang.org/pkg/net/http/

simple web server

Writing a simple web server is trivial in Go.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

import "net/http"

func hello(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello, Gophers!"))
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", hello)
	http.ListenAndServe(":3000", mux)
}

  • at line 3, we import net/http package which provides required types functions to build web server.
  • at line 10, we define a multiplexer which in responsible for routing incoming requests to various functions or handlers.It matches the URL of each incoming request against a list of registered patterns and calls the handler for the pattern that most closely matches the URL.
  • at line 11, we define HandleFunc which is way of defining incoming requests mapping to functions. We are using match-all / i.e. all the incoming requests will be handled by our single function.
  • HandleFunc is higher order function which accepts another function as its second parameter.
    func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
  • We are able to pass hello function to HandleFunc since signature of hello matches to that of function which HandleFunc accepts.
  • line 12 is responsible for starting web server at the specified port with specified multiplexer.
  • line 6 is responsible for returning response of Hello, Gophers! to every incoming request.

server struct

Alternatively, server struct can be used to construct web server.
Server struct is defined in Go Std library as

As you can see, there are lots of configuration options that can be provided while spinning new web server.
For up to date definition with comments, refer to : https://golang.org/src/net/http/server.go?s=77156:81268#L2480

using server struct

we can rewrite web server defined above using server struct as

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import "net/http"

func hello(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello, Gophers!"))
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", hello)
	httpServer := http.Server{
		Addr:    ":3000",
		Handler: mux,
	}

	httpServer.ListenAndServe()
}

This is very similar to one we defined earlier.

multiplexer

Multiplexer is responsible for defining routing of web application. Most of the times it is desired to run different logic based on url user is trying to access. Multiplexer are use to accomplish this task.
Lets see how we can register different handlers for different routes.

RouteHandler
/homehome
/aboutabout
/loginlogin
/logoutlogout

routes.go defines the registrations for all the routes in our application while handler.go defines the handler functions to be used.

Go will always try to match the More specific route matching url pattern irrespective of order in which they are registered.

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

import "net/http"

func main() {
registerRoutes()
httpServer := http.Server{
  Addr:    ":3000",
  Handler: mux,
}
httpServer.ListenAndServe()
}

routes.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import "net/http"

var mux = http.NewServeMux()

func registerRoutes() {
  mux.HandleFunc("/home", home)
  mux.HandleFunc("/about", about)
  mux.HandleFunc("/login", login)
  mux.HandleFunc("/logout", logout)
}

handler.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

import "net/http"

func about(w http.ResponseWriter, r *http.Request) {
  w.Write([]byte("about route"))
}

func home(w http.ResponseWriter, r *http.Request) {
  w.Write([]byte("home route"))
}
func login(w http.ResponseWriter, r *http.Request) {
  w.Write([]byte("login route"))
}
func logout(w http.ResponseWriter, r *http.Request) {
  w.Write([]byte("logout route"))
}

handler

Handler in Go are responsible for handling incoming http request. In multiplexer route registration, we have defined handlers for various routes.
We can define handlers in Go using two ways:

struct type as handler

General convention is to use a struct as handler for incoming http request. For a struct to be a valid handler, it needs to satisfy Handler interface which is defined as

1
2
3
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

Let’s create our own http handler struct.

  • Define struct type
1
type CustomHandler struct{}
  • provide implementation for ServeHTTP
1
2
3
func (hand *CustomHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("custom handler!"))
}

Now CustomerHandler can be used anywhere we need type of Handler interface.
Let’s spin a simple web server using this custom handler.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import "net/http"

type CustomHandler struct{}

func (hand *CustomHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("custom handler!"))
}

func main() {
	hand := CustomHandler{}
	mux := http.NewServeMux()
	mux.Handle("/", &hand)

	http.ListenAndServe(":3000", mux)
}

function as handler

We can also use functions as handlers in Go. This is made possible by HandlerFunc type defined in Go Std library.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// source : https://golang.org/src/net/http/server.go?s=61509:61556#L1993
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}
  • HandlerFunc type is an adapter that accepts a function and returns type of Handler interface by providing implementation for ServeHTTP.
  • HandlerFunc accepts any function which has ResponseWriter and *Request as its parameters.

Let’s spin up simple server using function as handlers.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import "net/http"

func functionHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("function as http handler!"))
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", functionHandler)
	http.ListenAndServe(":3000", mux)
}

This is how handlers and multiplexers work in Go.


Sumit
WRITTEN BY
Sumit
Gopher


What's on this Page