Default HTTP header in web API responses - http

Im attempting to set a default header in my Golang web api. I am using the net/http router that comes with Go. I can find many examples of how to do this :
Using some other routers.
Doing it for every response ( I am already doing it )
Is it there a way in which I can set a default header for all responses using net/http?

Create a wrapper function. Something like:
package main
import "net/http"
func SetDefaultHeaders(handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
// Set default headers
handler(w, r)
}
}
func main() {
http.HandleFunc("/", SetDefaultHeaders(func(w http.ResponseWriter, r *http.Request) {
// ...
}))
}

Related

How to server static files with virtual hosts functioality in Go

How can I server static files (with FileServer) for a virtual host in Go?
If I have my own handler function, the task seems to be easily solvable [1]:
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, world!")
})
http.HandleFunc("qa.example.com/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, improved world!")
})
http.ListenAndServe(":8080", nil)
}
But what if I need to serve static files (with FileServer)
for a virtual host?
This
r.PathPrefix("qa.example.com/").Handler(http.FileServer(http.Dir("/static/qa/")))
does not work — it is just ignored.
What am I doing wrong?
Is this approach generally wrong?
package main
import (
"embed"
"fmt"
"net/http"
"strings"
"time"
)
//go:embed *.go
var f embed.FS
func main() {
// embed.Fs defaule modtime use now or env value.
now := time.Now()
// use mapping host to http.FileSystem
vhosts := make(map[string]http.FileSystem)
vhosts["qa.example.com"] = http.FS(f) // from embed.FS
vhosts["my.example.com"] = http.Dir(".") // from http.Dir
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, world!")
})
http.HandleFunc("/static/", func(w http.ResponseWriter, r *http.Request) {
// find host
fs, ok := vhosts[r.Host]
if !ok {
w.WriteHeader(404)
w.Write([]byte("404 not found vhost"))
return
}
// open file from http.FileSystem
file, err := fs.Open(strings.TrimPrefix(r.URL.Path, "/static/"))
if err != nil {
// reference go1.18.3/net/http/fs.go toHTTPError function hander file error.
w.Write([]byte("check err is 403 or 404 or 500"))
return
}
stat, _ := file.Stat()
// fix embed modtime is zero.
modtime := stat.ModTime()
if modtime.IsZero() {
modtime = now
}
// response
http.ServeContent(w, r, stat.Name(), modtime, file)
})
http.ListenAndServe(":8080", nil)
}
run test exec command curl -H "Host: my.example.com" 127.0.0.1:8080/static/01.go, 01.go replacte your static filename.
Register a handler for host/path. Strip the /path part only when invoking the file handler.
This registration serves files for qa.example.com/static/* from the directory ./static/qa/.
http.HandleFunc("qa.example.com/static/", http.StripPrefix("/static", http.FileServer(http.Dir("./static/qa/")))

How to make http.redirect() to redirect automatically when executed

With this code it show me a link to click for redirect to /hello, how do i make it redirect without user interaction?
package main
import (
"fmt"
"net/http"
)
func main() {
r := http.NewServeMux()
r.HandleFunc("/", index)
r.HandleFunc("/hello", hello)
http.ListenAndServe(":80", r)
}
func index(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/hello", 200)
}
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Println("hello")
}
As already answered in the comments, the standard HTTP code to be returned in case of a redirect is 303. So the redirect line should be:
http.Redirect(w, r, "/hello", http.StatusSeeOther)
Also note, using http package http.XXX is preferred to writing HTTP codes directly

How to implement gRPC gateway mux handler to handle multiple http methods in Go

I have REST http handlers implemented with gorilla/mux. I am trying to migrate them into gRPC. There are some handlers doing file upload and download. So, my client decided to implement those handlers in gRPC gateway.
One of my mux handler handles multiple http methods and do stuffs according to http method in one handler func. Example code is like below.
package main
import (
"fmt"
"github.com/gorilla/mux"
"net/http"
)
func main() {
r := mux.NewRouter()
r.HandleFunc(`/doSomething`, func(writer http.ResponseWriter, request *http.Request) {
switch request.Method {
case http.MethodPost:
// create something
fmt.Fprint(writer, "POST")
case http.MethodGet:
// return something
fmt.Fprint(writer, "GET")
case http.MethodPut:
// update something
fmt.Fprint(writer, "PUT")
case http.MethodDelete:
// delete something
fmt.Fprint(writer, "DELETE")
}
})
http.ListenAndServe(`:5000`, r)
}
When I implemented similar grpc gateway mux handler to handle those requests with grpc-ecosystem/grpc-gateway/v2.3.0, I have to write separate handler functions to handle different http methods to same path. Example code like below.
package main
import (
"fmt"
"github.com/gorilla/mux"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"net/http"
)
func main() {
r := mux.NewRouter()
grpcGatewayHandler := runtime.NewServeMux()
r.PathPrefix("/").Handler(grpcGatewayHandler)
_ = grpcGatewayHandler.HandlePath(`POST`, `/doSomething`,
func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
fmt.Fprint(w, "POST")
})
_ = grpcGatewayHandler.HandlePath(`PUT`, `/doSomething`,
func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
fmt.Fprint(w, "PUT")
})
_ = grpcGatewayHandler.HandlePath(`GET`, `/doSomething`,
func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
fmt.Fprint(w, "GET")
})
_ = grpcGatewayHandler.HandlePath(`DELETE`, `/doSomething`,
func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
fmt.Fprint(w, "DELETE")
})
http.ListenAndServe(`:5000`, r)
}
I couldn't find any alternative solution to use different methods in same handler func.
Is there any way to handle different methods in one handler with
grpc-gateway?
Is there no difference between handling methods separately and
in one handler func?

Gorilla mux, best way to 'catch' response codes

I'm using Gorilla mux for all my routing. Now my app is working fine, I want to find a way to log all my response codes to -for example- statds. I have found this package: https://godoc.org/github.com/gorilla/handlers#LoggingHandler
Which allows me to output all responses into apache format. Although this is nice, it's not 100% what I want. I just want to extract the response statusses and send them to statds. Now what's the best/easiest way to achieve this?
package main
import (
"log"
"net/http"
"os"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/rogierlommers/mux-status-handler/articles"
"github.com/rogierlommers/mux-status-handler/users"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/products", articles.Handler)
r.HandleFunc("/users", users.Handler)
loggedRouter := handlers.LoggingHandler(os.Stdout, r)
log.Println("listening on 8080")
http.ListenAndServe(":8080", loggedRouter)
}
Above code gives me this:
So I'm looking for something similar, but instead of outputting the Apache access logs to stdout, I would like to be able to "do something" with the response code. I have also created a simple repo which contains my sample code. You can find it here.
I found this useful Blog post from Tim Andersson.
First he builds a new struct that satisfies the interface:
type loggingResponseWriter struct {
http.ResponseWriter
statusCode int
}
func NewLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter {
return &loggingResponseWriter{w, http.StatusOK}
}
func (lrw *loggingResponseWriter) WriteHeader(code int) {
lrw.statusCode = code
lrw.ResponseWriter.WriteHeader(code)
}
Then he's using it as a wrapper (or middleware):
func wrapHandlerWithLogging(wrappedHandler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
log.Printf("--> %s %s", req.Method, req.URL.Path)
lrw := NewLoggingResponseWriter(w)
wrappedHandler.ServeHTTP(lrw, req)
statusCode := lrw.statusCode
log.Printf("<-- %d %s", statusCode, http.StatusText(statusCode))
})
}
This is how it can be made with violetear, probably can give you a hint about how to deal with the status code within the handler:
package main
import (
"fmt"
"log"
"net/http"
"github.com/nbari/violetear"
)
func handleGET(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("I handle GET requests\n"))
// do anything here with the Status code
cw := w.(*violetear.ResponseWriter)
fmt.Printf("The status code is: %d\n", cw.Status())
}
func main() {
router := violetear.New()
router.HandleFunc("/", handleGET, "GET")
log.Fatal(http.ListenAndServe(":8080", router))
}
By using:
cw := w.(*violetear.ResponseWriter)
You can access the violetear.ResponseWriter which exposes the status code by using cw.Status()
You can write your own middleware, here's a very base example
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
"github.com/rogierlommers/mux-status-handler/articles"
"github.com/rogierlommers/mux-status-handler/users"
)
// middleWare ...
func middleWare(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// right not all this does is log like
// "github.com/gorilla/handlers"
log.Printf("%s %s %s", r.RemoteAddr, r.Method, r.URL)
// However since this is middleware you can have it do other things
// Examples, auth users, write to file, redirects, handle panics, ect
// add code to log to statds, remove log.Printf if you want
handler.ServeHTTP(w, r)
})
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/products", articles.Handler)
r.HandleFunc("/users", users.Handler)
log.Println("listening on 8080")
http.ListenAndServe(":8080", middleWare(r))
}

How can I rewrite the URL before it's being handled by the http package

With node/express its possible to do something like this
app.use(rewrite('/*', '/index.html'));
What would the equivalent in go be? I've tried using the httputil.ReverseProxy, but that seems entirely impractical.
For a simple "catch all" you can just do
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "index.html")
})
The "/" pattern matches everything.
For a more complex pattern, you need to wrap your mux with a handler that rewrites the url.
// register your handlers on a new mux
mux := http.NewServeMux()
mux.HandleFunc("/path", func(w http.ResponseWriter, r *http.Request) {
// your handler code
})
...
rewrite := func(path string) string {
// your rewrite code, returns the new path
}
...
// rewrite URL.Path in here before calling mux.ServeHTTP
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
r.URL.Path = rewrite(r.URL.Path)
mux.ServeHTTP(w,r)
})

Resources