I have an Handler like this:
type handler struct {
Services *domain.Services
Config *domain.Config
}
And then, a lot of new types (they can be twenty or more), like this:
type Handler1 handler
type Handler2 handler
And each one has a ServeHTTP method. And I use this so they can access the Services and Config variables.
They are being used in routes like this:
r.Handle("/login", &h.Handler1{
Services: s,
Config: c,
})
My question is: should I create all of this structs or just create a function that injects the Services and Config into the request Context and then I access them using r.Context().Value()?
I thought about doing this:
func handler1(w http.ResponseWriter, r *http.Request) {
s, c := r.Context().Value("services"), r.Context().Value("config")
// My code
}
r.HandleFunc("/login", inject(handler1, s, c))
What's the best/recommended?
As an alternative to creating all these handler types, you can have functions which return other functions (http.HandlerFunc). This way, you will create a closure and can access the parameters when the request arrives in the handler. For example:
package main
import (
"fmt"
"net/http"
)
func SomeHandler(conf SomeConfig, service SomeService) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "foobar config: %q", conf)
}
}
func main() {
// TODO: initialise your conf & service
http.HandleFunc("/somepath", SomeHandler(conf, service))
}
You probably could create some kind of Router which will provide ServeHTTP and do mapping between your real handlers and route paths.
Something like this:
package main
import "net/http"
type Router struct {
routes map[string]func(rw http.ResponseWriter, r *http.Request)
}
func NewRouter() *Router {
var r Router
r.routes = make(map[string]func(rw http.ResponseWriter, r *http.Request))
return &r
}
func (router *Router) addRoute(path string, f func(rw http.ResponseWriter, r *http.Request)) {
router.routes[path] = f
}
func (router *Router) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
for route, serveHTTP := range router.routes {
if route == r.URL.Path {
serveHTTP(rw, r)
return
}
}
rw.WriteHeader(http.StatusNotFound)
}
func teapot(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusTeapot)
}
func ok(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusOK)
}
func main() {
r := NewRouter()
r.addRoute("/teapot", teapot)
r.addRoute("/ok", ok)
http.ListenAndServe("localhost:8080", r)
}
Related
As I know, every requests to the server creates new goroutine. For ex (probably incorrect code, but this topic is not about it):
package main
import "net/http"
var exampleMap map[string]string
func handlerPost(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "POST":
{
exampleMap["test"] = test // Must I syncrhonise this writing?
}
case "GET":
{
if v, ok := exampleMap["test"] { // And what about reading?
fmt.Println(v)
}
}
}
}
func main() {
http.HandleFunc("/", handlerPost)
http.ListenAndServe(":8080", nil)
}
Does it mean that its unsafe to do it like this and I have to use sync.Map (for example), and what about if instead map here was a database queries? What can I do in this case. Thank you!
exampleMap is shared among goroutines, so you have to synchronize access to it. A mutex would do, a RWMutex would perform better:
var exampleMap map[string]string
var exampleMutex sync.RWMutex
...
exampleMutex.Lock()
exampleMap["test"] = test
exampleMutex.Unlock()
...
exampleMutex.RLock()
v, ok := exampleMap["test"]
exampleMutex.RUnlock()
if ok {
...
}
I am using socketio "github.com/googollee/go-socket.io" in Go.
For some reason, I have to make a new *socketio.server and register it to "/static/" which is already been registered with the old *socketio.server.
It means that I have to replace the handler ioserver in http.Handle("/socket.io/", ioserver) after the server has been started.
Below is part of my code, when something happens, I will create a new ioserver. I want the new ioserver to be the handler in http.Handle("/socket.io/", ioserver)
ioserver, _ = socketio.NewServer(nil)
http.Handle("/socket.io/", ioserver)
log.Fatal(http.ListenAndServe(":81", nil))
An application cannot swap handlers in the mux, but it can write a handler that swaps between other handlers:
type swapper struct {
mu sync.Mutex
h http.Handler
}
func (s *swapper) setHandler(h http.Handler) {
s.mu.Lock()
s.h = h
s.mu.Unlock()
}
func (w *swapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.mu.Lock()
h := s.h
s.mu.Unlock()
h.ServeHTTP(w, r)
}
Register it like this:
ioserver, _ = socketio.NewServer(nil)
s := &swapper{}
s.setHandler(ioserver)
http.Handle("/socket.io/", s)
log.Fatal(http.ListenAndServe(":81", nil))
At any time, the application can call s.setHandler to swap in a new handler.
I want to populate the logging context by items in the request, for example: r.Header.Get("X-Request-Id"). I assumed I could override the Log type in the handler from middleware. Though it doesn't seem to work and I am not sure why!
package main
import (
"fmt"
"net/http"
"os"
"github.com/apex/log"
"github.com/gorilla/mux"
)
// Assumption: handler is the shared state between the functions
type handler struct{ Log *log.Entry }
// New creates a handler for this application to co-ordinate shared resources
func New() (h handler) { return handler{Log: log.WithFields(log.Fields{"test": "FAIL"})} }
func (h handler) index(w http.ResponseWriter, r *http.Request) {
h.Log.Info("Hello from the logger")
fmt.Fprint(w, "YO")
}
func main() {
h := New()
app := mux.NewRouter()
app.HandleFunc("/", h.index)
app.Use(h.loggingMiddleware)
if err := http.ListenAndServe(":"+os.Getenv("PORT"), app); err != nil {
log.WithError(err).Fatal("error listening")
}
}
func (h handler) loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.Log = log.WithFields(log.Fields{"test": "PASS"})
next.ServeHTTP(w, r)
})
}
Can you see why h.Log = log.WithFields(log.Fields{"test": "PASS"}) doesn't seem to have any effect on h.Log.Info("Hello from the logger") which should be IIUC within the same request?
You need your logger to be request-scoped. You're setting it globally for the entire handler, every time a new connection comes in, which means you're asking for data races, and generally undesirable behavior.
For request-scoped context, the context.Context embedded in the request is perfect. You can access it through the Context() and WithContext methods.
Example:
var loggerKey = "Some unique key"
func (h handler) loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, loggerKey, log.WithFields(log.Fields{"test": "PASS"}))
next.ServeHTTP(w, r.WithContext(ctx)
})
}
Then to access your logger:
func doSomething(r *http.Request) error {
log, ok := r.Context().Value(loggerKey).(*log.Logger) // Or whatever type is appropriate
if !ok {
return errors.New("logger not set on context!")
}
// Do stuff...
}
I'm trying to get the following code to compile. I cannot successfully implement my own template handler struct an it results in the following error upon build.
Error:
./main.go:28:46: cannot use templateHandler literal (type *templateHandler) as type http.Handler in argument to http.Handle:
*templateHandler does not implement http.Handler (missing ServeHTTP method)
package main
import (
"html/template"
"log"
"net/http"
"path/filepath"
"sync"
)
// templ represents a single template
type templateHandler struct {
once sync.Once
filename string
templ *template.Template
}
// ServeHTTP handles the HTTP request.
func (t *templateHandler) ServerHTTP(w http.ResponseWriter, r *http.Request) {
t.once.Do(func() {
t.templ = template.Must(template.ParseFiles(filepath.Join("templates", t.filename)))
})
t.templ.Execute(w, nil)
}
func main() {
http.Handle("/", &templateHandler{filename: "chat.html"})
// Start Web Server
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal("ListenAndServe:", err)
}
}
Interface has the following representation.
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
You're misspelled with the name. ServerHTTP/ServeHTTP.
How is the following function implemented?
func handle(pattern string, handler interface{}) {
// ... what goes here? ...
http.Handle(pattern, ?)
}
handle("/foo", func(w http.ResponseWriter, r http.Request) { io.WriteString(w, "foo") }
handle("/bar", BarHandler{})
handle() is passed either a function which matches the type of http.HandlerFunc or a type which implements the http.Handler interface.
Instead of resorting to reflection, I would do it this way:
func handle(pattern string, handler interface{}) {
var h http.Handler
switch handler := handler.(type) {
case http.Handler:
h = handler
case func(http.ResponseWriter, *http.Request):
h = http.HandlerFunc(handler)
default:
// error
}
http.Handle(pattern, h)
}
First we need to introduce the term "reflections" in Java/C#'s terminology, RTTI in C++'s terminology. It's quite simple actually. The compiler keeps data to find out what is the type of an instance var i SomeType during runtime. Go supports reflection, and that's how it finds out what's the type of handler during runtime.
The handle function uses the reflection. A crude example
package main
import ("reflect";"http")
type fakeHandler struct{}
func (frw *fakeHandler) ServeHTTP(http.ResponseWriter, *http.Request) {}
func handle(pattern string, handler interface{}) {
handlerInterface := reflect.TypeOf(new(http.Handler)).Elem()
handlerFunction := reflect.TypeOf(new(http.HandlerFunc)).Elem()
t := reflect.TypeOf(handler)
if t.Implements(handlerInterface) {fmt.Println("http.Handler")}
//http.HandlerFunc is a different type than
// func(http.ResponseWriter, *http.Request), but we can do
// var hf HandlerFunc = func(http.ResponseWriter, *http.Request){}
if t.AssignableTo(handlerFunction) {fmt.Println("http.HandleFunc")}
}
func f(http.ResponseWriter, *http.Request) {}
func main() {
handle("",&fakeHandler{})
handle("",f)
}