How to call a route by its name from inside a handler? - go

How do I properly refer to route names from inside handlers?
Should mux.NewRouter() be assigned globally instead of standing inside a function?
func AnotherHandler(writer http.ResponseWriter, req *http.Request) {
url, _ := r.Get("home") // I suppose this 'r' should refer to the router
http.Redirect(writer, req, url, 302)
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/", HomeHandler).Name("home")
r.HandleFunc("/nothome/", AnotherHandler).Name("another")
http.Handle("/", r)
http.ListenAndServe(":8000", nil)
}

You have the method mux.CurrentRoute() that returns the route for a given request. From that request, you can create a subrouter and call Get("home")
Example: (play: http://play.golang.org/p/Lz10YUyP6e)
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func HomeHandler(writer http.ResponseWriter, req *http.Request) {
writer.WriteHeader(200)
fmt.Fprintf(writer, "Home!!!\n")
}
func AnotherHandler(writer http.ResponseWriter, req *http.Request) {
url, err := mux.CurrentRoute(req).Subrouter().Get("home").URL()
if err != nil {
panic(err)
}
http.Redirect(writer, req, url.String(), 302)
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/home", HomeHandler).Name("home")
r.HandleFunc("/nothome/", AnotherHandler).Name("another")
http.Handle("/", r)
http.ListenAndServe(":8000", nil)
}

Related

HTTP Middleware and Google Cloud Functions

What’s the equivalent to middleware handlers in Google Cloud Functions?
In standard approach, normally I do:
router.Handle("/receive", middlewares.ErrorHandler(MyReceiveHandler))
And then, in the middleware:
type ErrorHandler func(http.ResponseWriter, *http.Request) error
func (fn ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
err := fn(w, r)
if err == nil {
return
}
log.Printf("An error accured: %v", err)
clientError, ok := err.(errors.BaseError)
if !ok {
w.WriteHeader(500)
return
}
w.WriteHeader(clientError.GetStatusCode())
w.Write([]byte(clientError.Error()))
}
In AWS Lambda, I can achieve the same thing using, for example:
func main() {
lambda.Start(
middlewares.Authentication(Handler),
)
}
But I could not find a way to do this in GCP Functions.
How would it work?
Can you help me?
Let's say you start with the following server code in your development environment:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.Handle("/", MiddlewareFinalMsg(" Goodbye!", http.HandlerFunc(HelloWorld)))
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
func MiddlewareFinalMsg(msg string, h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.ServeHTTP(w, r)
fmt.Fprint(w, msg)
})
}
func HelloWorld(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
fmt.Fprint(w, "Hello, World!")
}
As far as I can tell, GCF requires its entry point to be an exported identifier of type func(http.ResponseWriter, *http.Request) (not http.HandlerFunc, not http.Handler); therefore, if you have a http.Handler, you'll need to select its ServeHTTP method explicitly to obtain a function of the expected type. However, that identifier can be a package-level function, a method, or a variable.
Here is how you can adapt the code above for GCF:
package p
import (
"fmt"
"net/http"
)
// use F as your GCF's entry point
var F = MiddlewareFinalMsg(" Goodbye!", http.HandlerFunc(HelloWorld)).ServeHTTP
func MiddlewareFinalMsg(msg string, h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.ServeHTTP(w, r)
fmt.Fprint(w, msg)
})
}
func HelloWorld(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
fmt.Fprint(w, "Hello, World!")
}

How to stop showing target URL in ReverseProxy in Golang using NewSingleHostReverseProxy()?

I am trying to have a reverse proxy in Golang, but I am unable to stop target ip from showing in browser ie. it is simply redirecting to wikipedia (target) rather than showing doing reverseproxy.
Can anyone tell what I am doing wrong?
package main
import (
"net/http"
"net/http/httputil"
"net/url"
"github.com/gorilla/mux"
)
func main() {
target := "https://www.wikipedia.org"
remote, err := url.Parse(target)
if err != nil {
panic(err)
}
proxy := httputil.NewSingleHostReverseProxy(remote)
r := mux.NewRouter()
r.HandleFunc("/forward/{rest:.*}", handler(remote, proxy))
http.ListenAndServe(":8080", r)
}
func handler(ur *url.URL, p *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
r.URL.Host = ur.Host
r.URL.Scheme = ur.Scheme
r.Host = ur.Host
r.URL.Path = mux.Vars(r)["rest"]
p.ServeHTTP(w, r)
}
}

Multiple Dir serving is not working

Any mistakes in below code? Multiple directory serving is not working from the below code. When I access the localhost:9090/ide, the server will return 404 error.
package main
import (
"log"
"net/http"
)
func serveIDE(w http.ResponseWriter, r *http.Request) {
http.FileServer(http.Dir("/home/user/ide")).ServeHTTP(w, r)
}
func serveConsole(w http.ResponseWriter, r *http.Request) {
http.FileServer(http.Dir("/home/user/console")).ServeHTTP(w, r)
}
func main() {
http.HandleFunc("/ide", serveIDE)
http.HandleFunc("/console", serveConsole)
err := http.ListenAndServe(":9090", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
When I change the code like this,
http.HandleFunc("/", serveIDE)
It will work as I expected.
One of the issues with using http.FileServer is that the request path is used to build the file name, so if you're serving from anywhere but the root you need to strip the route prefix to that handler.
The standard library includes a helpful tool for that http.StripPrefix, but that only works on http.Handlers, not http.HandleFuncs, so to use it you need to adapt your HandleFunc to a Handler.
Here is a working version that should do what you want. Note that wHandler is just an adapter from your HttpFunc methods to Hander interface:
package main
import (
"log"
"net/http"
)
func serveIDE(w http.ResponseWriter, r *http.Request) {
http.FileServer(http.Dir("/home/user/ide")).ServeHTTP(w, r)
}
func serveConsole(w http.ResponseWriter, r *http.Request) {
http.FileServer(http.Dir("/home/user/console")).ServeHTTP(w, r)
}
type wHandler struct {
fn http.HandlerFunc
}
func (h *wHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Printf("Handle request: %s %s", r.Method, r.RequestURI)
defer log.Printf("Done with request: %s %s", r.Method, r.RequestURI)
h.fn(w, r)
}
func main() {
http.Handle("/ide", http.StripPrefix("/ide", &wHandler{fn: serveIDE}))
http.Handle("/console", http.StripPrefix("/console", &wHandler{fn: serveConsole}))
err := http.ListenAndServe(":9090", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}

access post parameters in handler

I can access GET parameters using mux:
import (
"github.com/gorilla/mux"
)
func main(){
rtr := mux.NewRouter()
rtr.HandleFunc("/logon", logonGet).Methods("GET")
}
func logonGet(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
login := params["login"]
}
But cannot figure out how to access POST params
func main(){
rtr := mux.NewRouter()
rtr.HandleFunc("/logon", logonPost).Methods("POST")
}
func logonPost(w http.ResponseWriter, r *http.Request) {
// how to get POST parameters from request
}
By using (*http.Request).FormValue method.
func logonPost(w http.ResponseWriter, r *http.Request) {
login := r.FormValue("login")
// ...
}

How can I use HttpRouter with MuxChain?

I'd like to use httprouter with muxchain while keeping route parameters like /:user/.
Take the following example:
func log(res http.ResponseWriter, req *http.Request) {
fmt.Println("some logger")
}
func index(res http.ResponseWriter, req *http.Request) {
fmt.Fprintf(res, "Hi there, I love %s!", req.URL.Path[1:])
}
func main() {
logHandler := http.HandlerFunc(log)
indexHandler := http.HandlerFunc(index)
chain := muxchain.ChainHandlers(logHandler, indexHandler)
router := httprouter.New()
router.Handler("GET", "/:user", chain)
http.ListenAndServe(":8080", router)
}
When I visit http://localhost:8080/john I obviously don't have access to ps httprouter.Params
That's because httprouter needs to see type httprouter.Handle but the function is called with type http.Handler.
Is there any way to use both packages together? The HttpRouter GitHub repo says
The only disadvantage is, that no parameter values can be retrieved when a http.Handler or http.HandlerFunc is used, since there is no efficient way to pass the values with the existing function parameters.
If you strongly want to use that packages, you can try to do something like that:
package main
import (
"fmt"
"github.com/gorilla/context"
"github.com/julienschmidt/httprouter"
"github.com/stephens2424/muxchain"
"net/http"
)
func log(res http.ResponseWriter, req *http.Request) {
fmt.Printf("some logger")
}
func index(res http.ResponseWriter, req *http.Request) {
p := context.Get(req, "params").(httprouter.Params)
fmt.Fprintf(res, "Hi there, I love %s!", p.ByName("user"))
}
func MyContextHandler(h http.Handler) httprouter.Handle {
return func(res http.ResponseWriter, req *http.Request, p httprouter.Params) {
context.Set(req, "params", p)
h.ServeHTTP(res, req)
}
}
func main() {
logHandler := http.HandlerFunc(log)
indexHandler := http.HandlerFunc(index)
chain := muxchain.ChainHandlers(logHandler, indexHandler)
router := httprouter.New()
router.GET("/:user", MyContextHandler(chain))
http.ListenAndServe(":8080", router)
}
You would have to patch muxchain to accept httprouter.Handle, but it's rather simple to create your own chain handler, for example:
func chain(funcs ...interface{}) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
for _, h := range funcs {
switch h := h.(type) {
case httprouter.Handle:
h(w, r, p)
case http.Handler:
h.ServeHTTP(w, r)
case func(http.ResponseWriter, *http.Request):
h(w, r)
default:
panic("wth")
}
}
}
}
playground

Resources