SlideShare a Scribd company logo
Customising Your Own Web
Framework in Go
20 January 2015
Jonathan Gomez
Engineer, Zumata
This Talk
Overview
- Intro to serving requests with http/net
- Customising Handlers
- Writing Middleware
- Ecosystem
Key takeaways
- Compared with Ruby/Node.js, mainly using the standard library is considered normal
- Interfaces and first-class functions make it easy to extend functionality
- Ecosystem of libraries that work alongside http/netis growing
Intro to Serving Requests with http/net
Serving Requests via Standard Lib (1/4)
packagemain
import"net/http"
funchandlerFn(whttp.ResponseWriter,r*http.Request){
w.Write([]byte(`Helloworld!`))
}
funcmain(){
http.HandleFunc("/",handlerFn)
http.ListenAndServe("localhost:4000",nil)
}
ListenAndServe - creates server that will listen for requests
Each request spawns a go routine: goc.serve()
Serving Requests via Standard Lib (2/4)
ServeMux matches incoming request against a list of patterns (method/host/url)
ServeMux is a special kind of Handlerwhich calls another Handler
Handler interface
typeHandlerinterface{
ServeHTTP(ResponseWriter,*Request)
}
Serving Requests via Standard Lib (3/4)
Request handling logic in ordinary function func(ResponseWriter,*Request)
funcfinal(whttp.ResponseWriter,r*http.Request){
w.Write([]byte("OK"))
}
Register the function as a Handler on DefaultServeMux
http.Handle("/",http.HandlerFunc(final))
Also can:
http.HandleFunc("/",final)
Serving Requests via Standard Lib (4/4)
func(ResponseWriter,*Request)
ResponseWriter interface
typeResponseWriterinterface{
Header()Header
Write([]byte)(int,error)
WriteHeader(int)
}
Request struct
typeRequeststruct{
Methodstring
URL*url.URL
HeaderHeader
Bodyio.ReadCloser
ContentLengthint64
Hoststring
RemoteAddrstring
...
}
Customising Handlers
Demo: Customising Handlers - DRY Response Handling (1/3)
typeappHandlerstruct{
hfunc(http.ResponseWriter,*http.Request)(error)
}
func(ahappHandler)ServeHTTP(whttp.ResponseWriter,r*http.Request){
err:=ah.h(w,r)
iferr!=nil{
switcherr:=err.(type){
caseErrorDetails:
ErrorJSON(w,err)
default:
ErrorJSON(w,ErrorDetails{"InternalServerError","",500})
}
}
}
In app code we might extend this further:
Add error types and respond differently.
e.g. warn vs error-level log, send alerts, increment error metrics
Demo: Customising Handlers - DRY Response Handling (2/3)
typeErrorDetailsstruct{
Messagestring`json:"error"`
Detailsstring`json:"details,omitempty"`
Statusint`json:"-"`
}
func(eErrorDetails)Error()string{
returnfmt.Sprintf("Error:%s,Details:%s",e.Message,e.Details)
}
funcErrorJSON(whttp.ResponseWriter,detailsErrorDetails){
jsonB,err:=json.Marshal(details)
iferr!=nil{
http.Error(w,err.Error(),500)
return
}
w.Header().Set("Content-Type","application/json")
w.WriteHeader(details.Status)
w.Write(jsonB)
}
Demo: Customising Handlers - DRY Response Handling (3/3)
Use of special struct and special handler function to satisfy Handlerinterface
http.Handle("/",appHandler{unstableEndpoint})
Reduce repetition, extend functionality.
funcunstableEndpoint(whttp.ResponseWriter,r*http.Request)(error){
ifrand.Intn(100)>60{
returnErrorDetails{"Strangerequest","Pleasetryagain.",422}
}
ifrand.Intn(100)>80{
returnErrorDetails{"Seriousfailure","Weareinvestigating.",500}
}
w.Write([]byte(`{"ok":true}`))
returnnil
} Run
Demo: Customising Handlers - Avoiding Globals
Allows injecting dependencies rather than relying on global variables.
typeApistruct{
importantThingstring
//db*gorp.DbMap
//redis*redis.Pool
//logger...
}
typeappHandlerstruct{
*Api
hfunc(*Api,http.ResponseWriter,*http.Request)
}
func(ahappHandler)ServeHTTP(whttp.ResponseWriter,r*http.Request){
ah.h(ah.Api,w,r)
}
funcmyHandler(a*Api,whttp.ResponseWriter,r*http.Request){
w.Write([]byte("2015:Yearofthe"+a.importantThing))
} Run
Writing Middleware
Middleware: Why?
Abstract common functionality across a set of handlers
Bare minimum in Go:
func(nexthttp.Handler)http.Handler
Typical uses of middleware across languages/frameworks:
- logging
- authentication
- handling panic / exceptions
- gzipping
- request parsing
Demo: Middleware Example (Panic Recovery)
funcrecoveryMiddleware(nexthttp.Handler)http.Handler{
returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){
deferfunc(){
iferr:=recover();err!=nil{
log.Println("Recoverfromerror:",err)
http.Error(w,http.StatusText(500),500)
}
}()
log.Println("ExecutingrecoveryMiddleware")
next.ServeHTTP(w,r)
})
}
funcfinal(whttp.ResponseWriter,r*http.Request){
log.Println("ExecutingfinalHandler")
panic("walau!")
w.Write([]byte("OK"))
} Run
Demo: Chaining Middleware
funcmiddlewareOne(nexthttp.Handler)http.Handler{
returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){
log.Println("->ExecutingmiddlewareOne")
next.ServeHTTP(w,r)
log.Println("->ExecutingmiddlewareOneagain")
})
}
Calling chain of middleware
http.Handle("/",middlewareOne(middlewareTwo(http.HandlerFunc(final))))
funcmiddlewareTwo(nexthttp.Handler)http.Handler{
returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){
log.Println("--->ExecutingmiddlewareTwo")
next.ServeHTTP(w,r)
log.Println("--->ExecutingmiddlewareTwoagain")
})
} Run
Chaining Middleware - Alternate Syntax
3rd Party Library: Alice
Manages middleware with the standard function signature
Nice syntax for setting up chains used in different endpoints
chain:=alice.New(middlewareOne,middlewareTwo)
http.Handle("/",chain.Then(finalHandler))
Our example
noAuthChain:=alice.New(contextMiddleware,loggerMiddleware)
authChain:=alice.New(contextMiddleware,loggerMiddleware,apiKeyAuthMiddleware)
adminChain:=alice.New(contextMiddleware,loggerMiddleware,adminAuthMiddleware)
Demo: Creating Configurable Middleware
e.g. Pass the dependency on *AppLogger
varlogger*AppLogger=NewLogger()
loggerMiddleware:=simpleLoggerMiddlewareWrapper(logger)
http.Handle("/",loggerMiddleware(http.HandlerFunc(final)))
funcsimpleLoggerMiddlewareWrapper(logger*AppLogger)func(http.Handler)http.Handler{
returnfunc(nexthttp.Handler)http.Handler{
returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){
startTime:=time.Now()
next.ServeHTTP(w,r)
endTime:=time.Since(startTime)
logger.Info(r.Method+""+r.URL.String()+""+endTime.String())
})
}
} Run
Demo: Customising ResponseWriter (1/3)
typeResponseWriterinterface{
Header()http.Header
Write([]byte)(int,error)
WriteHeader(int)
}
ResponseWriter as an interface allows us to extend functionality easily
Example:
Step 1: Create a struct that wraps ResponseWriter
typeresponseWriterLoggerstruct{
w http.ResponseWriter
datastruct{
statusint
size int
}
}
Record data that would be otherwise be untracked.
Demo: Customising ResponseWriter (2/3)
Step 2: Define methods required for implicit satisfaction
func(l*responseWriterLogger)Header()http.Header{
returnl.w.Header()
}
func(l*responseWriterLogger)Write(b[]byte)(int,error){
//scenariowhereWriteHeaderhasnotbeencalled
ifl.data.status==0{
l.data.status=http.StatusOK
}
size,err:=l.w.Write(b)
l.data.size+=size
returnsize,err
}
func(l*responseWriterLogger)WriteHeader(codeint){
l.w.WriteHeader(code)
l.data.status=code
}
Demo: Customising ResponseWriter (3/3)
funcspecialLoggerMiddlewareWrapper(logger*AppLogger)func(http.Handler)http.Handler{
returnfunc(nexthttp.Handler)http.Handler{
returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){
startTime:=time.Now()
w2:=&responseWriterLogger{w:w}
next.ServeHTTP(w2,r)
logger.Info(r.Method+""+r.URL.String()+""+
time.Since(startTime).String()+
"status:"+strconv.Itoa(w2.data.status)+
"size:"+strconv.Itoa(w2.data.size))
})
}
} Run
Growing Middleware Ecosystem
Excerpt from Negroni Github page
graceful:(https://github.com/stretchr/graceful)graceful HTTP Shutdown
oauth2:(https://github.com/goincremental/negroni-oauth2)oAuth2 middleware
binding:(https://github.com/mholt/binding)data binding from HTTP requests into structs
xrequestid:(https://github.com/pilu/xrequestid)Assign a random X-Request-Id: header to each request
gorelic:(https://github.com/jingweno/negroni-gorelic)New Relic agent for Go runtime
Mailgun's Oxy
stream:(http://godoc.org/github.com/mailgun/oxy/stream)retries and buffers requests and responses
connlimit:(http://godoc.org/github.com/mailgun/oxy/connlimit)Simultaneous connections limiter
ratelimit:(http://godoc.org/github.com/mailgun/oxy/ratelimit)Rate limiter
Other Web Framework Components
Routing & Extracting URL Params
- standard library can be inflexible
- regex for extracting url params can feel too low level
- plenty of third party routers, e.g. Gorilla mux
funcShowWidget(whttp.ResponseWriter,r*http.Request){
vars:=mux.Vars(r)
teamIdStr:=vars["team_id"]
widgetIdStr:=vars["widget_id"]
...
}
Request-specific context
- sharing data between items in middleware chain and final handler
- solutions involve either global map, or per-request map/structs using custom
handlers/middleware
Web frameworks vs Build on top of standard library?
Time/expertise to build what you need? Too much re-inventing?
Your optimisation vs framework optimisation?
Performance? Does performance order of magnitude matter?
How much magic do you want?
Compatibility with net/http/ ecosystem? Framework interchangeability?
Martini -- 6.1k(https://github.com/go-martini/martini)
Revel -- 4.7k(https://github.com/revel/revel)
beego -- 3.7k(https://github.com/astaxie/beego)
goji -- 1.9k(https://github.com/zenazn/goji)
gin -- 1.9k(https://github.com/gin-gonic/gin)
negroni -- 1.8k(https://github.com/codegangsta/negroni)
go-json-rest -- 1.1k(https://github.com/ant0ine/go-json-rest)
Gorilla/mux -- 1.1k(https://github.com/gorilla/mux)
Tiger Tonic -- 0.8k(https://github.com/rcrowley/go-tigertonic)
Gocraft/web -- 0.6k(https://github.com/gocraft/web)
Thank you
Jonathan Gomez
Engineer, Zumata
jonathanbgomez@gmail.com(mailto:jonathanbgomez@gmail.com)
@jonog(http://twitter.com/jonog)

More Related Content

What's hot (20)

KEY
Non blocking io with netty
Zauber
 
PDF
The basics of fluentd
Treasure Data, Inc.
 
PDF
Like loggly using open source
Thomas Alrin
 
PDF
Dive into Fluentd plugin v0.12
N Masahiro
 
PDF
JRuby with Java Code in Data Processing World
SATOSHI TAGOMORI
 
PDF
Fluentd meetup
Sadayuki Furuhashi
 
PDF
Centralized + Unified Logging
Gabor Kozma
 
PDF
Introduction tomcat7 servlet3
JavaEE Trainers
 
PDF
Fluentd meetup #2
Treasure Data, Inc.
 
PDF
The basics of fluentd
Treasure Data, Inc.
 
PPTX
Elk with Openstack
Arun prasath
 
PDF
Netty: asynchronous data transfer
Victor Cherkassky
 
PDF
WebTalk - Implementing Web Services with a dedicated Java daemon
Geert Van Pamel
 
PDF
Fluentd v1.0 in a nutshell
N Masahiro
 
PDF
服务框架: Thrift & PasteScript
Qiangning Hong
 
PDF
Fluentd unified logging layer
Kiyoto Tamura
 
PDF
Fluentd v0.12 master guide
N Masahiro
 
PDF
Troubleshooting RabbitMQ and services that use it
Michael Klishin
 
PDF
Fluentd meetup dive into fluent plugin (outdated)
N Masahiro
 
PDF
Fluentd - Set Up Once, Collect More
Sadayuki Furuhashi
 
Non blocking io with netty
Zauber
 
The basics of fluentd
Treasure Data, Inc.
 
Like loggly using open source
Thomas Alrin
 
Dive into Fluentd plugin v0.12
N Masahiro
 
JRuby with Java Code in Data Processing World
SATOSHI TAGOMORI
 
Fluentd meetup
Sadayuki Furuhashi
 
Centralized + Unified Logging
Gabor Kozma
 
Introduction tomcat7 servlet3
JavaEE Trainers
 
Fluentd meetup #2
Treasure Data, Inc.
 
The basics of fluentd
Treasure Data, Inc.
 
Elk with Openstack
Arun prasath
 
Netty: asynchronous data transfer
Victor Cherkassky
 
WebTalk - Implementing Web Services with a dedicated Java daemon
Geert Van Pamel
 
Fluentd v1.0 in a nutshell
N Masahiro
 
服务框架: Thrift & PasteScript
Qiangning Hong
 
Fluentd unified logging layer
Kiyoto Tamura
 
Fluentd v0.12 master guide
N Masahiro
 
Troubleshooting RabbitMQ and services that use it
Michael Klishin
 
Fluentd meetup dive into fluent plugin (outdated)
N Masahiro
 
Fluentd - Set Up Once, Collect More
Sadayuki Furuhashi
 

Viewers also liked (7)

PPT
Cleanppp
mkoller
 
KEY
Fronteers: UX in een Agile omgeving
Folkert Jong
 
PPT
IdentityPR Social Media 101 Presentation
Identity
 
PPT
Portfolio
earlenescheid
 
PPT
Social Media Hr Advantage Preso V2
Identity
 
PPT
How to Think Like a Social Media Cowboy
Identity
 
PPT
Networking Presentation
Identity
 
Cleanppp
mkoller
 
Fronteers: UX in een Agile omgeving
Folkert Jong
 
IdentityPR Social Media 101 Presentation
Identity
 
Portfolio
earlenescheid
 
Social Media Hr Advantage Preso V2
Identity
 
How to Think Like a Social Media Cowboy
Identity
 
Networking Presentation
Identity
 
Ad

Similar to Customising Your Own Web Framework In Go (20)

PDF
Knot.x: when Vert.x and RxJava meet
Tomasz Michalak
 
PPTX
Everything you wanted to know about writing async, concurrent http apps in java
Baruch Sadogursky
 
PDF
Native REST Web Services with Oracle 11g
Marcelo Ochoa
 
PPTX
Intro to Node
Aaron Stannard
 
PPTX
Net/http and the http.handler interface
Joakim Gustin
 
PPTX
Net/http and the http.handler interface
Evolve
 
PPTX
Java 8 Feature Preview
Jim Bethancourt
 
PDF
Advancedservletsjsp
Pradeep Raikwar
 
ODP
Finagle and Java Service Framework at Pinterest
Pavan Chitumalla
 
PPT
nodejs_at_a_glance.ppt
WalaSidhom1
 
PDF
Woo: Writing a fast web server @ ELS2015
fukamachi
 
KEY
A language for the Internet: Why JavaScript and Node.js is right for Internet...
Tom Croucher
 
PPT
nodejs_at_a_glance, understanding java script
mohammedarshadhussai4
 
PPTX
L11 Process Design
Ólafur Andri Ragnarsson
 
PPT
Server side JavaScript: going all the way
Oleg Podsechin
 
PDF
Extending Retrofit for fun and profit
Matthew Clarke
 
PDF
Networked APIs with swift
Tim Burks
 
PDF
Web Server and how we can design app in C#
caohansnnuedu
 
PDF
Nodejs a-practical-introduction-oredev
Felix Geisendörfer
 
PDF
Frameworks
Ryan Levick
 
Knot.x: when Vert.x and RxJava meet
Tomasz Michalak
 
Everything you wanted to know about writing async, concurrent http apps in java
Baruch Sadogursky
 
Native REST Web Services with Oracle 11g
Marcelo Ochoa
 
Intro to Node
Aaron Stannard
 
Net/http and the http.handler interface
Joakim Gustin
 
Net/http and the http.handler interface
Evolve
 
Java 8 Feature Preview
Jim Bethancourt
 
Advancedservletsjsp
Pradeep Raikwar
 
Finagle and Java Service Framework at Pinterest
Pavan Chitumalla
 
nodejs_at_a_glance.ppt
WalaSidhom1
 
Woo: Writing a fast web server @ ELS2015
fukamachi
 
A language for the Internet: Why JavaScript and Node.js is right for Internet...
Tom Croucher
 
nodejs_at_a_glance, understanding java script
mohammedarshadhussai4
 
L11 Process Design
Ólafur Andri Ragnarsson
 
Server side JavaScript: going all the way
Oleg Podsechin
 
Extending Retrofit for fun and profit
Matthew Clarke
 
Networked APIs with swift
Tim Burks
 
Web Server and how we can design app in C#
caohansnnuedu
 
Nodejs a-practical-introduction-oredev
Felix Geisendörfer
 
Frameworks
Ryan Levick
 
Ad

Recently uploaded (20)

PPTX
Innowell Capability B0425 - Commercial Buildings.pptx
regobertroza
 
PPTX
MPMC_Module-2 xxxxxxxxxxxxxxxxxxxxx.pptx
ShivanshVaidya5
 
PPTX
UNIT DAA PPT cover all topics 2021 regulation
archu26
 
PPTX
Structural Functiona theory this important for the theorist
cagumaydanny26
 
PPTX
The Role of Information Technology in Environmental Protectio....pptx
nallamillisriram
 
PPTX
MobileComputingMANET2023 MobileComputingMANET2023.pptx
masterfake98765
 
PPTX
Electron Beam Machining for Production Process
Rajshahi University of Engineering & Technology(RUET), Bangladesh
 
PPTX
Benefits_^0_Challigi😙🏡💐8fenges[1].pptx
akghostmaker
 
PDF
BioSensors glucose monitoring, cholestrol
nabeehasahar1
 
PPT
Oxygen Co2 Transport in the Lungs(Exchange og gases)
SUNDERLINSHIBUD
 
PPTX
Break Statement in Programming with 6 Real Examples
manojpoojary2004
 
PPTX
265587293-NFPA 101 Life safety code-PPT-1.pptx
chandermwason
 
PDF
Additional Information in midterm CPE024 (1).pdf
abolisojoy
 
PDF
POWER PLANT ENGINEERING (R17A0326).pdf..
haneefachosa123
 
PDF
Water Design_Manual_2005. KENYA FOR WASTER SUPPLY AND SEWERAGE
DancanNgutuku
 
PDF
monopile foundation seminar topic for civil engineering students
Ahina5
 
PDF
MOBILE AND WEB BASED REMOTE BUSINESS MONITORING SYSTEM
ijait
 
PPTX
drones for disaster prevention response.pptx
NawrasShatnawi1
 
PDF
Introduction to Productivity and Quality
মোঃ ফুরকান উদ্দিন জুয়েল
 
PPTX
Introduction to Neural Networks and Perceptron Learning Algorithm.pptx
Kayalvizhi A
 
Innowell Capability B0425 - Commercial Buildings.pptx
regobertroza
 
MPMC_Module-2 xxxxxxxxxxxxxxxxxxxxx.pptx
ShivanshVaidya5
 
UNIT DAA PPT cover all topics 2021 regulation
archu26
 
Structural Functiona theory this important for the theorist
cagumaydanny26
 
The Role of Information Technology in Environmental Protectio....pptx
nallamillisriram
 
MobileComputingMANET2023 MobileComputingMANET2023.pptx
masterfake98765
 
Electron Beam Machining for Production Process
Rajshahi University of Engineering & Technology(RUET), Bangladesh
 
Benefits_^0_Challigi😙🏡💐8fenges[1].pptx
akghostmaker
 
BioSensors glucose monitoring, cholestrol
nabeehasahar1
 
Oxygen Co2 Transport in the Lungs(Exchange og gases)
SUNDERLINSHIBUD
 
Break Statement in Programming with 6 Real Examples
manojpoojary2004
 
265587293-NFPA 101 Life safety code-PPT-1.pptx
chandermwason
 
Additional Information in midterm CPE024 (1).pdf
abolisojoy
 
POWER PLANT ENGINEERING (R17A0326).pdf..
haneefachosa123
 
Water Design_Manual_2005. KENYA FOR WASTER SUPPLY AND SEWERAGE
DancanNgutuku
 
monopile foundation seminar topic for civil engineering students
Ahina5
 
MOBILE AND WEB BASED REMOTE BUSINESS MONITORING SYSTEM
ijait
 
drones for disaster prevention response.pptx
NawrasShatnawi1
 
Introduction to Productivity and Quality
মোঃ ফুরকান উদ্দিন জুয়েল
 
Introduction to Neural Networks and Perceptron Learning Algorithm.pptx
Kayalvizhi A
 

Customising Your Own Web Framework In Go

  • 1. Customising Your Own Web Framework in Go 20 January 2015 Jonathan Gomez Engineer, Zumata
  • 2. This Talk Overview - Intro to serving requests with http/net - Customising Handlers - Writing Middleware - Ecosystem Key takeaways - Compared with Ruby/Node.js, mainly using the standard library is considered normal - Interfaces and first-class functions make it easy to extend functionality - Ecosystem of libraries that work alongside http/netis growing
  • 3. Intro to Serving Requests with http/net
  • 4. Serving Requests via Standard Lib (1/4) packagemain import"net/http" funchandlerFn(whttp.ResponseWriter,r*http.Request){ w.Write([]byte(`Helloworld!`)) } funcmain(){ http.HandleFunc("/",handlerFn) http.ListenAndServe("localhost:4000",nil) } ListenAndServe - creates server that will listen for requests Each request spawns a go routine: goc.serve()
  • 5. Serving Requests via Standard Lib (2/4) ServeMux matches incoming request against a list of patterns (method/host/url) ServeMux is a special kind of Handlerwhich calls another Handler Handler interface typeHandlerinterface{ ServeHTTP(ResponseWriter,*Request) }
  • 6. Serving Requests via Standard Lib (3/4) Request handling logic in ordinary function func(ResponseWriter,*Request) funcfinal(whttp.ResponseWriter,r*http.Request){ w.Write([]byte("OK")) } Register the function as a Handler on DefaultServeMux http.Handle("/",http.HandlerFunc(final)) Also can: http.HandleFunc("/",final)
  • 7. Serving Requests via Standard Lib (4/4) func(ResponseWriter,*Request) ResponseWriter interface typeResponseWriterinterface{ Header()Header Write([]byte)(int,error) WriteHeader(int) } Request struct typeRequeststruct{ Methodstring URL*url.URL HeaderHeader Bodyio.ReadCloser ContentLengthint64 Hoststring RemoteAddrstring ... }
  • 9. Demo: Customising Handlers - DRY Response Handling (1/3) typeappHandlerstruct{ hfunc(http.ResponseWriter,*http.Request)(error) } func(ahappHandler)ServeHTTP(whttp.ResponseWriter,r*http.Request){ err:=ah.h(w,r) iferr!=nil{ switcherr:=err.(type){ caseErrorDetails: ErrorJSON(w,err) default: ErrorJSON(w,ErrorDetails{"InternalServerError","",500}) } } } In app code we might extend this further: Add error types and respond differently. e.g. warn vs error-level log, send alerts, increment error metrics
  • 10. Demo: Customising Handlers - DRY Response Handling (2/3) typeErrorDetailsstruct{ Messagestring`json:"error"` Detailsstring`json:"details,omitempty"` Statusint`json:"-"` } func(eErrorDetails)Error()string{ returnfmt.Sprintf("Error:%s,Details:%s",e.Message,e.Details) } funcErrorJSON(whttp.ResponseWriter,detailsErrorDetails){ jsonB,err:=json.Marshal(details) iferr!=nil{ http.Error(w,err.Error(),500) return } w.Header().Set("Content-Type","application/json") w.WriteHeader(details.Status) w.Write(jsonB) }
  • 11. Demo: Customising Handlers - DRY Response Handling (3/3) Use of special struct and special handler function to satisfy Handlerinterface http.Handle("/",appHandler{unstableEndpoint}) Reduce repetition, extend functionality. funcunstableEndpoint(whttp.ResponseWriter,r*http.Request)(error){ ifrand.Intn(100)>60{ returnErrorDetails{"Strangerequest","Pleasetryagain.",422} } ifrand.Intn(100)>80{ returnErrorDetails{"Seriousfailure","Weareinvestigating.",500} } w.Write([]byte(`{"ok":true}`)) returnnil } Run
  • 12. Demo: Customising Handlers - Avoiding Globals Allows injecting dependencies rather than relying on global variables. typeApistruct{ importantThingstring //db*gorp.DbMap //redis*redis.Pool //logger... } typeappHandlerstruct{ *Api hfunc(*Api,http.ResponseWriter,*http.Request) } func(ahappHandler)ServeHTTP(whttp.ResponseWriter,r*http.Request){ ah.h(ah.Api,w,r) } funcmyHandler(a*Api,whttp.ResponseWriter,r*http.Request){ w.Write([]byte("2015:Yearofthe"+a.importantThing)) } Run
  • 14. Middleware: Why? Abstract common functionality across a set of handlers Bare minimum in Go: func(nexthttp.Handler)http.Handler Typical uses of middleware across languages/frameworks: - logging - authentication - handling panic / exceptions - gzipping - request parsing
  • 15. Demo: Middleware Example (Panic Recovery) funcrecoveryMiddleware(nexthttp.Handler)http.Handler{ returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){ deferfunc(){ iferr:=recover();err!=nil{ log.Println("Recoverfromerror:",err) http.Error(w,http.StatusText(500),500) } }() log.Println("ExecutingrecoveryMiddleware") next.ServeHTTP(w,r) }) } funcfinal(whttp.ResponseWriter,r*http.Request){ log.Println("ExecutingfinalHandler") panic("walau!") w.Write([]byte("OK")) } Run
  • 16. Demo: Chaining Middleware funcmiddlewareOne(nexthttp.Handler)http.Handler{ returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){ log.Println("->ExecutingmiddlewareOne") next.ServeHTTP(w,r) log.Println("->ExecutingmiddlewareOneagain") }) } Calling chain of middleware http.Handle("/",middlewareOne(middlewareTwo(http.HandlerFunc(final)))) funcmiddlewareTwo(nexthttp.Handler)http.Handler{ returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){ log.Println("--->ExecutingmiddlewareTwo") next.ServeHTTP(w,r) log.Println("--->ExecutingmiddlewareTwoagain") }) } Run
  • 17. Chaining Middleware - Alternate Syntax 3rd Party Library: Alice Manages middleware with the standard function signature Nice syntax for setting up chains used in different endpoints chain:=alice.New(middlewareOne,middlewareTwo) http.Handle("/",chain.Then(finalHandler)) Our example noAuthChain:=alice.New(contextMiddleware,loggerMiddleware) authChain:=alice.New(contextMiddleware,loggerMiddleware,apiKeyAuthMiddleware) adminChain:=alice.New(contextMiddleware,loggerMiddleware,adminAuthMiddleware)
  • 18. Demo: Creating Configurable Middleware e.g. Pass the dependency on *AppLogger varlogger*AppLogger=NewLogger() loggerMiddleware:=simpleLoggerMiddlewareWrapper(logger) http.Handle("/",loggerMiddleware(http.HandlerFunc(final))) funcsimpleLoggerMiddlewareWrapper(logger*AppLogger)func(http.Handler)http.Handler{ returnfunc(nexthttp.Handler)http.Handler{ returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){ startTime:=time.Now() next.ServeHTTP(w,r) endTime:=time.Since(startTime) logger.Info(r.Method+""+r.URL.String()+""+endTime.String()) }) } } Run
  • 19. Demo: Customising ResponseWriter (1/3) typeResponseWriterinterface{ Header()http.Header Write([]byte)(int,error) WriteHeader(int) } ResponseWriter as an interface allows us to extend functionality easily Example: Step 1: Create a struct that wraps ResponseWriter typeresponseWriterLoggerstruct{ w http.ResponseWriter datastruct{ statusint size int } } Record data that would be otherwise be untracked.
  • 20. Demo: Customising ResponseWriter (2/3) Step 2: Define methods required for implicit satisfaction func(l*responseWriterLogger)Header()http.Header{ returnl.w.Header() } func(l*responseWriterLogger)Write(b[]byte)(int,error){ //scenariowhereWriteHeaderhasnotbeencalled ifl.data.status==0{ l.data.status=http.StatusOK } size,err:=l.w.Write(b) l.data.size+=size returnsize,err } func(l*responseWriterLogger)WriteHeader(codeint){ l.w.WriteHeader(code) l.data.status=code }
  • 21. Demo: Customising ResponseWriter (3/3) funcspecialLoggerMiddlewareWrapper(logger*AppLogger)func(http.Handler)http.Handler{ returnfunc(nexthttp.Handler)http.Handler{ returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){ startTime:=time.Now() w2:=&responseWriterLogger{w:w} next.ServeHTTP(w2,r) logger.Info(r.Method+""+r.URL.String()+""+ time.Since(startTime).String()+ "status:"+strconv.Itoa(w2.data.status)+ "size:"+strconv.Itoa(w2.data.size)) }) } } Run
  • 22. Growing Middleware Ecosystem Excerpt from Negroni Github page graceful:(https://github.com/stretchr/graceful)graceful HTTP Shutdown oauth2:(https://github.com/goincremental/negroni-oauth2)oAuth2 middleware binding:(https://github.com/mholt/binding)data binding from HTTP requests into structs xrequestid:(https://github.com/pilu/xrequestid)Assign a random X-Request-Id: header to each request gorelic:(https://github.com/jingweno/negroni-gorelic)New Relic agent for Go runtime Mailgun's Oxy stream:(http://godoc.org/github.com/mailgun/oxy/stream)retries and buffers requests and responses connlimit:(http://godoc.org/github.com/mailgun/oxy/connlimit)Simultaneous connections limiter ratelimit:(http://godoc.org/github.com/mailgun/oxy/ratelimit)Rate limiter
  • 23. Other Web Framework Components Routing & Extracting URL Params - standard library can be inflexible - regex for extracting url params can feel too low level - plenty of third party routers, e.g. Gorilla mux funcShowWidget(whttp.ResponseWriter,r*http.Request){ vars:=mux.Vars(r) teamIdStr:=vars["team_id"] widgetIdStr:=vars["widget_id"] ... } Request-specific context - sharing data between items in middleware chain and final handler - solutions involve either global map, or per-request map/structs using custom handlers/middleware
  • 24. Web frameworks vs Build on top of standard library? Time/expertise to build what you need? Too much re-inventing? Your optimisation vs framework optimisation? Performance? Does performance order of magnitude matter? How much magic do you want? Compatibility with net/http/ ecosystem? Framework interchangeability? Martini -- 6.1k(https://github.com/go-martini/martini) Revel -- 4.7k(https://github.com/revel/revel) beego -- 3.7k(https://github.com/astaxie/beego) goji -- 1.9k(https://github.com/zenazn/goji) gin -- 1.9k(https://github.com/gin-gonic/gin) negroni -- 1.8k(https://github.com/codegangsta/negroni) go-json-rest -- 1.1k(https://github.com/ant0ine/go-json-rest) Gorilla/mux -- 1.1k(https://github.com/gorilla/mux) Tiger Tonic -- 0.8k(https://github.com/rcrowley/go-tigertonic) Gocraft/web -- 0.6k(https://github.com/gocraft/web)
  • 25. Thank you Jonathan Gomez Engineer, Zumata [email protected](mailto:[email protected]) @jonog(http://twitter.com/jonog)