Add custom ServerMux to a http.Handle - go

I am creating a module which will provide a http.Handler that can be attached to any running server.
My module has a lot of bundled handlerFuncs for handling internal requests:
func NewHandler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/one", func(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("one"))
})
mux.HandleFunc("/two", func(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("two"))
})
return mux
}
This handler can be attached to a custom path:
package main
import (
"net/http"
"github.com/someuser/someproject"
)
func main() {
http.Handle("/path", someproject.NewHandler())
http.ListenAndServe(":8080", nil)
}
On request invocation to http://localhost:8080/path/one I get 404. What am I doing wrong?

Related

Send custom response in gorilla mux when route method does not match

how can I send a custom response when the route method (HTTP Verb) does not match?
When I hit the following route in a post method
r.handleFunc("/destination", handler).Methods('GET')
I want to receive (let's assume its a JSON response)
{
status: "ERROR",
message: "Route method not supported."
}
The idea is that I don't want to have each handler with the route.Method == $METHOD check. Looking for a way where I can define once and apply to each route.
To setup custom return for route methods, you could simply override the handler "MethodNotAllowedHandler" with your own.
Example:
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
func main() {
log.Fatal(http.ListenAndServe(":8080", router()))
}
func router() *mux.Router {
r := mux.NewRouter()
r.HandleFunc("/destination", destination).Methods("GET")
r.MethodNotAllowedHandler = MethodNotAllowedHandler()
return r
}
func destination(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "destination output")
}
func MethodNotAllowedHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Method not allowed")
})
}
Check out some the gorilla/handler repository. It contains middleware handlers (e.g. handlers that are executed before your main handler) including a handler for checking whether a HTTP method is allowed. E.g.:
MethodHandler{
"GET": myHandler,
}
Any other method will automatically return a 405 Method not allowed response.

golang simple static server print on terminal

this is my initial golang code :
package main
import (
"net/http"
"io"
)
const hello = `hello world`
func helloHandler(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, hello)
}
func main() {
http.HandleFunc("/", helloHandler)
http.ListenAndServe(":1088", nil)
}
it is a simple http server, i need add new function, every get request print in linux terminal ip, METHOD, /request.
example output in terminal need:
95.250.33.36 GET /
95.250.33.36 GET /favicon.ico
95.250.33.36 GET /robots.txt
how i can do this ?
The best thing about Golang is interfaces.
Your helloHandler actually implements the HandlerFunc interface.
Using the Open/Close Principle we can take helloHandler and extend it for logging the request in the following way:
func wrapHandlerWithLogging(wrappedHandler http.Handler) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
log.Printf("--> %s %s", req.Method, req.URL.Path)
wrappedHandler.ServeHTTP(w, req)
})
}
func main() {
...
http.HandleFunc("/", wrapHandlerWithLogging(http.HandlerFunc(helloHandler)))
...
}
So basically, we wrap helloHandler which implements HandlerFunc with another HandlerFunc.
In this example, we only log the request method (GET, POST, PUT and etc) and the request path (e.g. '/'). However, you can log other data:
req.RemoteAddr network address that sent the request
req.Proto the protocol version
req.Host specifies the host on which URL is sought

Extending GoLang's http.ResponseWriter functionality to pre/post process responses

I am trying to write a simple http MiddleWare handler that will process an http response. Unfortunately, it does not work and I cannot figure out what mistake I am making. Any/all help is appreciated!
I am using Go Gorilla mux router
Here are illustrative parts of the code:
import (
"fmt"
"log"
"github.com/gorilla/mux"
)
:
func Start() {
router := mux.NewRouter()
router.HandleFunc("/", myHandler)
:
log.Fatal(http.ListenAndServe(":8088", Middleware(router)))
}
func myHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "myHandler called")
}
func Middleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
neww := NewProcessor(w)
h.ServeHTTP(neww, r)
})
}
type Processor struct {
http.ResponseWriter
}
func (r *Processor) Write(b []byte) (int, error) {
fmt.Printf("******* Processor writing...")
log.Print(string(b)) // log it out
return r.Write(b) // pass it to the original ResponseWriter
}
func NewProcessor(w http.ResponseWriter) http.ResponseWriter {
fmt.Printf("******* Creating new Processor...")
return &Processor{ResponseWriter: w}
}
The output I get is listed below (extra logging text omitted for clarity):
******* Creating new Processor
myHandler called
However, notice the message "******* Processor writing..." was not displayed, suggesting that the "Write" function did not get called.
What changes need to be made to allow the "Write" function to be called?
return r.Write(b) caused an infinite loop of calls to the Processor's Write() method. Replacing it with return r.ResponseWriter.Write(b) fixed the bug.
Here is the corrected code:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", myHandler)
log.Fatal(http.ListenAndServe(":8088", Middleware(mux)))
}
func myHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "myHandler called")
}
func Middleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
new := NewProcessor(w)
h.ServeHTTP(new, r)
})
}
type Processor struct {
http.ResponseWriter
}
func (r *Processor) Write(b []byte) (int, error) {
log.Print("******* Processor writing...")
log.Print(string(b)) // log it out
return r.ResponseWriter.Write(b) // pass it to the original ResponseWriter
}
func NewProcessor(w http.ResponseWriter) http.ResponseWriter {
log.Print("******* Creating new Processor...")
return &Processor{ResponseWriter: w}
}
Output:
2016/07/21 22:59:08 ******* Creating new Processor...
2016/07/21 22:59:08 ******* Processor writing...
2016/07/21 22:59:08 myHandler called

how to organize gorilla mux routes?

i am using Gorilla Mux for writing a REST API and i am having trouble organizing my routes, currently all of my routes are defined in the main.go file like this
//main.go
package main
import (
"NovAPI/routes"
"fmt"
"github.com/gorilla/mux"
"net/http"
)
func main() {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/hello", func(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Hello")
})
router.HandleFunc("/user", func(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "User")
})
router.HandleFunc("/route2", func(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Route2")
})
router.HandleFunc("/route3", func(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Route3")
})
// route declarations continue like this
http.ListenAndServe(":1128", router)
}
so what i want to do is take out and split this route declaration into multiple files, how would i go about doing that? thanks in advance.
You can modularize your routers independently into different packages, and mount them on the main router
Elaborating just a little on the following issue, you can come up with this approach, that makes it quite scalable (and easier to test, to some degree)
/api/router.go
package api
import (
"net/http"
"github.com/gorilla/mux"
)
func Router() *mux.Router {
router := mux.NewRouter()
router.HandleFunc("/", home)
return router
}
func home(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("hello from API"))
}
/main.go
package main
import (
"log"
"net/http"
"strings"
"github.com/...yourPath.../api"
"github.com/...yourPath.../user"
"github.com/gorilla/mux"
)
func main() {
router := mux.NewRouter()
router.HandleFunc("/", home)
mount(router, "/api", api.Router())
mount(router, "/user", user.Router())
log.Fatal(http.ListenAndServe(":8080", router))
}
func mount(r *mux.Router, path string, handler http.Handler) {
r.PathPrefix(path).Handler(
http.StripPrefix(
strings.TrimSuffix(path, "/"),
handler,
),
)
}
func home(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("Home"))
}
What about something like this ?
//main.go
package main
import (
"NovAPI/routes"
"fmt"
"github.com/gorilla/mux"
"net/http"
)
func main() {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/hello", HelloHandler)
router.HandleFunc("/user", UserHandler)
router.HandleFunc("/route2", Route2Handler)
router.HandleFunc("/route3", Route3Handler)
// route declarations continue like this
http.ListenAndServe(":1128", router)
}
func HelloHandler(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Hello")
}
func UserHandler(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "User")
}
func Route2Handler(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Route2")
}
func Route3Handler(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Route3")
}
That way you can put your handlers in other files, or even other packages.
If you endup with additionnal dependencies like a database, you can even avoid the need of the global var using a constructor trick:
//main.go
func main() {
db := sql.Open(…)
//...
router.HandleFunc("/hello", NewHelloHandler(db))
//...
}
func NewHelloHandler(db *sql.DB) func(http.ResponseWriter, *http.Request) {
return func(res http.ResponseWriter, req *http.Request) {
// db is in the local scope, and you can even inject it to test your
// handler
fmt.Fprintln(res, "Hello")
}
}
I like checking out other projects in github to grab ideas on how to do stuff, and for these cases I usually take a look first at the Docker repo. This is the way they do it:
For the system's routes, define all handlers in system_routes.go and then initialize those routes on a NewRouter function in system.go.
type systemRouter struct {
backend Backend
routes []router.Route
}
func NewRouter(b Backend) router.Router {
r := &systemRouter{
backend: b,
}
r.routes = []router.Route{
local.NewOptionsRoute("/", optionsHandler),
local.NewGetRoute("/_ping", pingHandler),
local.NewGetRoute("/events", r.getEvents),
local.NewGetRoute("/info", r.getInfo),
local.NewGetRoute("/version", r.getVersion),
local.NewPostRoute("/auth", r.postAuth),
}
return r
}
// Routes return all the API routes dedicated to the docker system.
func (s *systemRouter) Routes() []router.Route {
return s.routes
}
Notice that systemRouter implements the router.Router interface and the Routes function returns a []router.Route, and their handlers are defined as
func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error
instead of Go's standard http handler:
func(w http.ResponseWriter, r *http.Request)
So there's extra code of theirs to convert a Docker API Handler to a Go HTTP Handler at the makeHttpHandler function.
And finally, to add those routes to their mux router, on their server.go they implement several other functions to add middleware to their handlers.
If this is something that you think it's what you are looking for, then take your time to analyze the Docker code for their routes, and if you need me to elaborate more or if I missed anything, post a comment.
Since I am new to Go, I prefer a less verbose solution. In each module, we can create a Route function that expects a main route pointer and creates sub-routes to it. Our main.go file would be as follows
package main
import (
"net/http"
"github.com/user-name/repo-name/auth"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
auth.Router(r)
http.ListenAndServe(":8080", r)
}
then in auth module, we can create a route file
package auth
import "github.com/gorilla/mux"
func Router(r *mux.Router) {
routes := r.PathPrefix("/auth").Subrouter()
routes.HandleFunc("/register", Register)
}

Golang http mux change handler function

I am fairly new to Go and have not been able to find any information on this, maybe it is just not possible at this time.
I am trying to delete or replace a mux route (using http.NewServeMux, or gorilla's mux.Router). My end goal is to be able to enable/disable a route or set of routes without having to restart the program.
I can probably accomplish this on a handler to handler basis and just return 404 if that feature is "disabled", but I would rather find a more general way to do this since I would like to implement it for every route in my application.
Or would I be better off just keeping track of disabled url patterns and using some middleware to prevent handler execution?
If someone can at least point me in the right direction, I will absolutely post code examples of a solution assuming there is one. Thanks!
There's no built in way, but it is easy enough to implement play.
type HasHandleFunc interface { //this is just so it would work for gorilla and http.ServerMux
HandleFunc(pattern string, handler func(w http.ResponseWriter, req *http.Request))
}
type Handler struct {
http.HandlerFunc
Enabled bool
}
type Handlers map[string]*Handler
func (h Handlers) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
if handler, ok := h[path]; ok && handler.Enabled {
handler.ServeHTTP(w, r)
} else {
http.Error(w, "Not Found", http.StatusNotFound)
}
}
func (h Handlers) HandleFunc(mux HasHandleFunc, pattern string, handler http.HandlerFunc) {
h[pattern] = &Handler{handler, true}
mux.HandleFunc(pattern, h.ServeHTTP)
}
func main() {
mux := http.NewServeMux()
handlers := Handlers{}
handlers.HandleFunc(mux, "/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("this will show once"))
handlers["/"].Enabled = false
})
http.Handle("/", mux)
http.ListenAndServe(":9020", nil)
}
Yes you can.
One way to do it is to have a sturct that implement http.Handle interface with the method
ServeHTTP.
Then have the struct contain another muxer like gorilla's
and finally have an atomic Switch to enable/ disable the subrouting
This is a working example of what I mean:
package main
import (
"fmt"
"github.com/gorilla/mux"
"net/http"
"sync/atomic"
)
var recording int32
func isRecording() bool {
return atomic.LoadInt32(&recording) != 0
}
func setRecording(shouldRecord bool) {
if shouldRecord {
atomic.StoreInt32(&recording, 1)
} else {
atomic.StoreInt32(&recording, 0)
}
}
type SwitchHandler struct {
mux http.Handler
}
func (s *SwitchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if isRecording() {
fmt.Printf("Switch Handler is Recording\n")
s.mux.ServeHTTP(w, r)
return
}
fmt.Printf("Switch Handler is NOT Recording\n")
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "NOT Recording\n")
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/success/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Recording\n")
})
handler := &SwitchHandler{mux: router}
setRecording(false)
http.Handle("/", handler)
http.ListenAndServe(":8080", nil)
}
According to https://github.com/gorilla/mux/issues/82 it is suggested to swap the router instead of deleting routes. Existing connections will stay open.

Resources