Writing

HTTP servers

with

Go

Basic http server stuff

  • HTTP Routes
  • Middlewares
  • Logging
  • Database

HTTP Routes


func sayHello(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Say hello"))
}

HTTP Routes

Attached to a Router

package main

import (
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("/hello", sayHello)
	log.Fatal(
		http.ListenAndServe(":8080", nil),
	)
}

func sayHello(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Say hello"))
}
func TestHealthCheckHandler(t *testing.T) {
    // Create a request to pass to our handler.
    // We don't have any query parameters for now, so we'll
    // pass 'nil' as the third parameter.
    req, err := http.NewRequest("GET", "/health", nil)
    if err != nil {
        t.Fatal(err)
    }

    // We create a ResponseRecorder 
    // (which satisfies http.ResponseWriter) to record the response.
    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(HealthCheckHandler)

    // Our handlers satisfy http.Handler, so we can call their ServeHTTP method
    // directly and pass in our Request and ResponseRecorder.
    handler.ServeHTTP(rr, req)

    // Check the status code is what we expect.
    if status := rr.Code; status != http.StatusOK {
        t.Errorf("handler returned wrong status code: got %v want %v",
            status, http.StatusOK)
    }
}

MUX

Middlewares

but first...

HIGHER ORDER FUNCTIONS

 

????


type SUM func(a ...int) int

func GetOp() SUM {
	return func(ints ...int) int {
		sum := 0
		for _, x := range ints {
			sum += x
		}
		return sum
	}
}

func main() {
	summer := GetOp()
	sum := summer(1, 2, 3)
	println(sum)
func main() {
	http.Handle("/hello", Middleware(http.HandlerFunc(SayHello)))
	http.ListenAndServe(":8080", nil)
}

func AuthenticateReq(r *http.Request) bool {
	return true
}

func Middleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Println("authing")
		if AuthenticateReq(r) {
			next.ServeHTTP(w, r)
		}
		w.WriteHeader(401)
	})
}

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

Or...

you could use alice to avoid weird looking code

http.Handle("/hello", M1(M2(M3(http.HandlerFunc(SayHello)))))

becomes

http.Handle("/hello", alice.New(M1,M2,M3).ThenFunc(SayHello))




Logging

https://github.com/sirupsen/logrus

Interfacing external dependencies

type Logger interface {
	InfoLog(v ...interface{})
}

type logger struct {
	*logrus.Logger
}

func NewLogger() Logger {
	return &logger{logrus.New()}
}

func (l *logger) InfoLog(v ...interface{}) {
	l.Info(v)
}
func main() {
	l := NewLogger()
	http.HandleFunc("/hello", SayHello(l))
	http.ListenAndServe(":8080", nil)
}

func SayHello(l Logger) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		l.InfoLog("saying hello")
		w.Write([]byte("hello!"))
	}
}

Usage

Database

    import "database/sql"

            vs


import "github.com/jinzhu/gorm"

Conn

import (
  "github.com/jinzhu/gorm"
  _ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
  db, err := gorm.Open("mysql", "<connectionString>")
  defer db.Close()
}

Query

// Struct

db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)

// SELECT * FROM users WHERE name = "jinzhu" 
// AND age = 20 LIMIT 1;
type Store interface {
    GetUser(ID string) (User, error)    
}


//All three should implement the GetUser method

type PostgresDB struct{}
type BoltDB struct{}
type SomeOtherDb struct{}


Interfacing for Scalability


func NewStore(dialect string, conf DbConf) Store {
    
    switch(dialect) {
        case "postgres":
            return NewPostgresStore(conf)
        ...
        ...
    }
    

}


func main() {
    conf := GetConf()
    store := NewStore("postgres", conf)
    http.HandleFunc("/user", UserLogin(conf, store))
    http.ListenAndServe(":8080", nil)
}


//UserLogin UserLogin
func UserLogin(conf *conf.Configuration, store store.Store) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        user, err := store.GetUser(ID)
            if err != nil {
                        ...            
            }
     }
}
Made with Slides.com