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
Related
I am making simple web app using http/server and I use following code for handling routes (credit to this post):
package retable
import (
"context"
"fmt"
"net/http"
"regexp"
"strconv"
"strings"
)
var routes = []route{
newRoute("GET", "/", home),
}
func newRoute(method, pattern string, handler http.HandlerFunc) route {
return route{method, regexp.MustCompile("^" + pattern + "$"), handler}
}
type route struct {
method string
regex *regexp.Regexp
handler http.HandlerFunc
}
func Serve(w http.ResponseWriter, r *http.Request) {
var allow []string
for _, route := range routes {
matches := route.regex.FindStringSubmatch(r.URL.Path)
if len(matches) > 0 {
if r.Method != route.method {
allow = append(allow, route.method)
continue
}
ctx := context.WithValue(r.Context(), ctxKey{}, matches[1:])
route.handler(w, r.WithContext(ctx))
return
}
}
if len(allow) > 0 {
w.Header().Set("Allow", strings.Join(allow, ", "))
http.Error(w, "405 method not allowed", http.StatusMethodNotAllowed)
return
}
http.NotFound(w, r)
}
type ctxKey struct{}
func getField(r *http.Request, index int) string {
fields := r.Context().Value(ctxKey{}).([]string)
return fields[index]
}
func home(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "home\n")
}
How to serve static files from local "static/" folder on "/" endpoint if other route registration on this endpoint already exists?
As written, your code expects an exact match for the pattern provided when constructing a route. (See the ^ and $ when constructing the regex.) So you will not be able to handle /static/ requests in the /-pattern's handler.
You may be able to achieve what you want if you make changes to your existing code though. Some options below.
Option 1
Include the static pattern in routes:
var routes = []route{
newRoute("GET", "/", home),
newRoute("GET", "/static/(.+)", static),
}
Define an example static HTTP handler function:
func static(w http.ResponseWriter, r *http.Request) {
log.Println("received static request for ", getField(r, 0))
}
You may want to use a combination of the following to faciliate serving static files:
http.StripPrefix
http.FileServer
http.Dir / http.FS, which embed plays well with
Option 2
Modify newRoute to not use ^ and $ when constructing the regex. However, this may affect expectations elsewhere in your code. Particularly, the / pattern will match all requests, so the ordering of the routes slice becomes important.
return route{method, regexp.MustCompile(pattern), handler}
Then in home:
func home(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/static/") {
log.Println("received static request", r.URL.Path)
// TODO: actually respond with file
return
}
fmt.Fprint(w, "home\n")
}
Footnote
As a footnote, I would recommend using http.ServeMux / http.DefaultServeMux instead of your Serve implementation. These are battle-tested, will likely be more performant, and may likely have less-surprising behavior than your code.
For instance, http.ServeMux/http.DefaultServeMux clean paths, which the code in the question does not do. So, for example, with the original code as in the question, a request for
curl localhost:8080//
will result in a 404 due to the double slash instead of reaching the home handler.
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?
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.
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)
}
I'm trying to set an http header for multiple handlers. My first thought was to make a custom write function that would set the header before writing the response like the code sample at the bottom.
However, when I pass a pointer to the http.ResponseWriter and try to access it from my function it tells me that "type *http.ResponseWriter has no Header method".
What is the best way to set headers for multiple handlers, and also why isn't the pointer working the way I want it to?
func HelloServer(w http.ResponseWriter, req *http.Request) {
type Message struct {
Name string
Body string
Time int64
}
m := Message{"Alice", "Hello", 1294706395881547000}
b, _ := json.Marshal(m)
WriteJSON(&w, b)
}
func WriteJSON(wr *http.ResponseWriter, rawJSON []byte) {
*wr.Header().Set("Content-Type", "application/json")
io.WriteString(*wr, string(rawJSON))
}
func main() {
http.HandleFunc("/json", HelloServer)
err := http.ListenAndServe(":9000", nil)
if err != nil {
log.Fatal("ListenAndServer: ", err)
}
}
I'm not sure about the multiple handlers thing, but I do know why the code you wrote is failing. The key is that the line:
*wr.Header().Set("Content-Type", "application/json")
is being interpreted, because of operator precedence, as:
*(wr.Header().Set("Content-Type", "application/json"))
Since wr has the type *http.ResponseWriter, which is a pointer to and interface, rather than the interface itself, this won't work. I assume that you knew that, which is why you did *wr. I assume what you meant to imply to the compiler is:
(*wr).Header().Set("Content-Type", "application/json")
If I'm not mistaken, that should compile and behave properly.
You don't need to use *wr as it already references a pointer.
wr.Header().Set("Content-Type", "application/json") should be sufficient.
If you want to set "global" headers for every request, you can create a function that satisfies http.HandleFunc (go.auth has a good example) and then wrap your handlers like so:
http.HandleFunc("/hello", Defaults(helloHandler))
Also take a look at the net/http documentation, which has further examples.
I wrap my handlers with an error handler
which calls my AddSafeHeader function.
I based it on http://golang.org/doc/articles/error_handling.html
but it doesn't use ServeHTTP so it works with appstats:
http.Handle("/", appstats.NewHandler(util.ErrorHandler(rootHandler)))
Here:
package httputil
import (
"appengine"
"net/http"
"html/template"
)
func AddSafeHeaders(w http.ResponseWriter) {
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-XSS-Protection", "1; mode=block")
w.Header().Set("X-Frame-Options", "SAMEORIGIN")
w.Header().Set("Strict-Transport-Security", "max-age=2592000; includeSubDomains")
}
// Redirect to a fixed URL
type redirectHandler struct {
url string
code int
}
func (rh *redirectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Redirect(w, r, rh.url, rh.code)
}
func Redirect(w http.ResponseWriter, r *http.Request, urlStr string, code int) {
AddSafeHeaders(w)
http.Redirect(w, r, urlStr, code)
}
// RedirectHandler returns a request handler that redirects
// each request it receives to the given url using the given
// status code.
func RedirectHandler(url string, code int) http.Handler {
return &redirectHandler{url, code}
}
func ErrorHandler(fn func(appengine.Context, http.ResponseWriter, *http.Request)) func(appengine.Context, http.ResponseWriter, *http.Request) {
return func(c appengine.Context, w http.ResponseWriter, r *http.Request) {
defer func() {
if err, ok := recover().(error); ok {
c.Errorf("%v", err)
w.WriteHeader(http.StatusInternalServerError)
errorTemplate.Execute(w, err)
}
}()
AddSafeHeaders(w)
fn(c, w, r)
}
}
// Check aborts the current execution if err is non-nil.
func Check(err error) {
if err != nil {
panic(err)
}
}
var errorTemplate = template.Must(template.New("error").Parse(errorTemplateHTML))
const errorTemplateHTML = `
<html>
<head>
<title>XXX</title>
</head>
<body>
<h2>An error occurred:</h2>
<p>{{.}}</p>
</body>
</html>
`
http.ResponseWriter is an interface.
You should probably not be using a pointer to an interface. In net/http/server.go, the unexported response struct is the actual type that implements ResponseWriter when the server calls your handler, and importantly, when the server actually calls the handler's ServeHTTP, it passes a *response. It's already a pointer, but you don't see that because ResonseWriter is an interface. (the response pointer is created here, by (c *conn).readRequest. (The links will likely be for the wrong lines the future, but you should be able to locate them).
That's why the ServeHTTP function required to implement Handler is:
ServeHTTP(w ResponseWriter, r *Request)
i.e. not a pointer to ResponseWriter, as this declaration already permits a pointer to a struct that implements the ResponseWriter interface.
As I am new to Go, I created a minimal contrived example, based on elithrar's answer, which shows how to easily add headers to all your routes / responses. We do so, by creating a function that satisfies the http.HandlerFunc interface, then wraps the route handler functions:
package main
import (
"encoding/json"
"log"
"net/http"
"github.com/gorilla/mux"
)
// Hello world.
func Hello(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode("Hello World")
}
// HelloTwo world
func HelloTwo(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode("Hello Two World")
}
// JSONHeaders conforms to the http.HandlerFunc interface, and
// adds the Content-Type: application/json header to each response.
func JSONHeaders(handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
handler(w, r)
}
}
func main() {
router := mux.NewRouter()
// Now, instead of calling your handler function directly, pass it into the wrapper function.
router.HandleFunc("/", JSONHeaders(Hello)).Methods("GET")
router.HandleFunc("/hellotwo", JSONHeaders(HelloTwo)).Methods("GET")
log.Fatal(http.ListenAndServe(":3000", router))
}
Results:
$ go run test.go &
$ curl -i localhost:3000/
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 28 Feb 2019 22:27:04 GMT
Content-Length: 14
"Hello World"
What i end up doing:
// Accepts a user supplied http.HandlerFunc and then modifies it in various ways. In this case, it adds two new headers.
func CommonlHandler(h http.HandlerFunc) http.HandlerFunc {
return func (rs http.ResponseWriter, rq *http.Request) {
rs.Header().Add("Server", "Some server")
rs.Header().Add("Cache-Control", "no-store")
h(rs, rq)
}
// somewhere down the line, where you're setting up http request handlers
serveMux := http.NewServeMux()
serveMux.HandleFunc("/", CommonHandler(func(rs http.ResponseWriter, rq *http.Request) {
// Handle request as usual. Since it's wrapped in the CommonHandler and we've set some headers there, responses to requests to "/" will contain those headers.
// Be mindful what modifications you're doing here. If, for ex., you decide you want to apply different caching strategy than the Common one, since this will be passed to the CommonHandler, your changes will be overwritten and lost. So it may be a good idea to introduce checks in CommonHandler to determine whether the header exists, before you decide to create it.
}))
serveMux.HandleFunc("/contact", CommonHandler(func(rs http.ResponseWriter, rq *http.Request) {
// handle another request as usual
}))