golang building a simple server is pretty simple, here are three ways to rewrite it from the ground up internally

  • The first program uses the default DefaultServeMux and default Server structure.
  • The second will use a custom handler
  • The third will use a custom Server structure

In terms of execution efficiency, the third one is the fastest to execute, after all, it’s unencapsulated.

golang http server

The program below starts a web server at your localhost port 8080. It has one route, namely the / route. If you open the url in the browser, it will output “version 1”.

package main

import (
        "io"
        "log"
        "net/http"
)
func main() {
        // Set routing rules
        http.HandleFunc("/", Tmp)

        //Use the default DefaultServeMux.
        err := http.ListenAndServe(":8080", nil)
        if err != nil {
                log.Fatal(err)
        }
}

func Tmp(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "version 1")
}

This is the most common way to create a server without thinking too much about it!

golang http server

Routes

You can create more than one route. Mux is used to control access to the route by implementing a Handler registration into Mux.

package main

import (
    "io"
    "log"
    "net/http"
)

type myHandler struct{}

func (*myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "URL: "+r.URL.String())
}

func Tmp(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "version 2")
}


func main(){
    mux := http.NewServeMux()

    // Register routes and register handlers in this form.
    mux.Handle("/",&myHandler{})
    
    mux.HandleFunc("/tmp", Tmp)
    
    //http.ListenAndServe uses the default server structure.
    err := http.ListenAndServe(":8080", mux)
    if err != nil {
        log.Fatal(err)
    }
}

golang http router

Listening and implementing http services with a custom server. In the examle below we define a route like mux["/tmp"].

package main

import (
    "io"
    "log"
    "net/http"
    "time"
)

//Define a map to implement routing table.
var mux map[string]func(http.ResponseWriter , *http.Request) 

func main(){
    server := http.Server{
        Addr: ":8080",
        Handler: &myHandler{},
        ReadTimeout: 5*time.Second,
    }
    
    mux = make(map[string]func(http.ResponseWriter, *http.Request))
    mux["/tmp"] = Tmp
    err := server.ListenAndServe()
    if err != nil {
        log.Fatal(err)
    }   
}

type myHandler struct{}

func (*myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){
    // Implement route forwarding
    if h, ok := mux[r.URL.String()];ok{
    //Implement route forwarding with this handler, the corresponding route calls the corresponding func.
        h(w, r)
        return
    }
    io.WriteString(w, "URL: "+r.URL.String())
}

func Tmp(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "version 3")
}

The above three ways are actually the unpacking of the encapsulation, using a custom way to rewrite the underlying structure, you can better understand the net/http package.