Put the http.Handler in martini - go

How do I integrate just like http.FileServer with martini?
` package main
import (
"github.com/go-martini/martini"
"net/http"
)
func main() {
m := martini.Classic()
//http.Handle("/", http.FileServer(http.Dir("."))) //It doesn't work!
m.Run()
}`

I believe the FileServer isn't used directly in Martini: see issues/20:
Unfortunately The fileserver middleware throws a 404 if there is no match, which means we will need to roll our own
Hence PR 26 and commit a945713 in static.go that you can see in static_test.go
m := New()
r := NewRouter()
m.Use(Static(currentRoot))
m.Action(r.Handle)

Related

Blank page when using swaggo/http-swagger with julienschmidt/httprouter

I am making an api and used swaggo/swag to build a swagger interface. Previously, I used the net/http package, and everything was working fine.
I switched to julienschmidt/httprouter, but I don't manage to make the swagger interface work again. Here is my code
package main
import (
"fmt"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
httpSwagger "github.com/swaggo/http-swagger"
)
func main() {
router := httprouter.New()
router.ServeFiles("/api/doc/static/*filepath", http.Dir("api/swagger/static"))
router.HandlerFunc(http.MethodGet, "/api/doc/index.html", swaggerHandler)
// router.HandlerFunc(http.MethodGet, "/api/doc", swaggerHandler)
fmt.Println("Server on port 8080")
log.Fatal(http.ListenAndServe(":8080", router))
}
func swaggerHandler(w http.ResponseWriter, r *http.Request) {
swaggerFileUrl := "http://localhost:8080/api/doc/static/swagger.json"
handler := httpSwagger.Handler(httpSwagger.URL(swaggerFileUrl))
handler.ServeHTTP(w, r)
}
I checked if swaggerFileUrl variable is correct, and I am able to access the json file with this url. The interface is a complete blank page titled "Swagger UI". Because the title is replaced, I am assuming, that something happened, but I don't know if the issue comes from httpSwagger or httprouter.
Edit: Issue is caused because javascript files loading the interface are not present. See this github issue
You can do it like this:
routes := httprouter.New()
routes.GET("/doc/:any", swaggerHandler)
func swaggerHandler(res http.ResponseWriter, req *http.Request, p httprouter.Params) {
httpSwagger.WrapHandler(res, req)
}
Do not forget import doc files
import (
_ "example.project/docs"
)

How to access another file in GO

I'm trying to access a controller from main.go but I'm getting the following error:
./main.go:34:28: cannot refer to unexported name controllers.getUserDetails
./main.go:34:28: undefined: controllers.getUserDetails
here's a snippet of my main.go, I've removed some extra code
package main
import (
"net/http"
"os"
"log"
"github.com/urfave/negroni"
"github.com/gorilla/mux"
"github.com/joho/godotenv"
"Go-Social/controllers"
)
func main() {
router := mux.NewRouter()
UserRouter := router.PathPrefix("/api/user").Subrouter()
UserRouter.HandleFunc("", controllers.getUserDetails).Methods("GET")
env := os.Getenv("GO_ENV")
if "" == env {
env = "Development"
}
// appending middlewares
server := negroni.Classic()
// router handler with negroni
server.UseHandler(router)
// starting server
server.Run(":" + os.Getenv(env + "_PORT"))
}
my controller.go file
package controllers
import (
"net/http"
"fmt"
)
func getUserDetails(w http.ResponseWriter, r *http.Request) {
fmt.Println("here")
message := "Hello World"
w.Write([]byte(message))
}
Please Help I'm new to Go. Thanks in advance.
to use a function from another package, you need to export it (GetUserDetails)
as said here
An identifier may be exported to permit access to it from another package
func GetUserDetails(w http.ResponseWriter, r *http.Request) {
fmt.Println("here")
message := "Hello World"
w.Write([]byte(message))
}
Since the getUserDetails function is in another package it cannot be accessed. Only functions starting with capital letter can be accessed. That's how encapsulation works in Go.
func GetUserDetails(w http.ResponseWriter, r *http.Request) {
fmt.Println("here")
message := "Hello World"
w.Write([]byte(message))
}
So in your main:
UserRouter.HandleFunc("", controllers.GetUserDetails).Methods("GET")
Language like Java, enCAPSulation in class-based OOP is achieved through private and public class variables / methods.
In Go, encapsulation is achieved on a package level.
In other words, in Go, starting with capital letter for any package object (type, variable or function) will allow you to access it from another package.

Sharing a gorilla/mux router across packages

I'm having some issues with implementing a slight MVC design with gorilla/mux.
The layout of the modules is as follows:
main.go
-- controllers
---- base.controller.go
---- example.controller.go
-- models
---- base.model.go
---- example.controller.go
All the files in controllers is in the controllers package, same with models and then the main.go is the main package.
Currently I'm just trying to get the Base Controller to be able to be shared with the main package which is working, although it's throwing some errors when trying to implement routes. The build is not throwing any errors, but the routes are not available. If I implement the Walk function in the Gorilla/Mux documentation to print out all the registered routes for the mux.Router then it gives me this error:
&{%!!(MISSING)s(*mux.Router=&{ [0xc4200901b0] map[] true
false false false}) %!!(MISSING)s(http.HandlerFunc=0xc8df0)
[%!!(MISSING)s(*mux.routeRegexp=&{/ false false true false
0xc420095360 / [] []})] %!!(MISSING)s(*mux.routeRegexpGroup=&{
0xc420016240 []}) %!!(MISSING)s(bool=true) %!!(MISSING)s(bool=false)
%!!(MISSING)s(bool=false) %!!(MISSING)s(bool=false)
%!!(MISSING)s(mux.BuildVarsFunc=)}
The reasoning for the global var V1Router *mux.Router is firstly to access it in the main package and also to create subrouters in the other controllers.
I am fairly new to Go, but I'm trying my best to learn the best practices! Any help would be greatly appreciated!
Example code below:
base.controllers.go
package controllers
import (
"fmt"
"bytes"
"net/http"
"github.com/gorilla/mux"
)
var V1Router *mux.Router
func init () {
V1Router = mux.NewRouter()
V1Router.StrictSlash(true)
V1Router.HandleFunc("/", BaseHandler)
}
// Base route to access the API Documentation.
func BaseHandler (w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, Gophers!")
}
main.go
package main
import (
"net/http"
"log"
"github.com/projectrepo/project/models"
"github.com/projectrepo/project/controllers"
"github.com/gorilla/mux"
)
func main () {
http.Handle("/v1", controllers.V1Router)
if err := http.ListenAndServe(":8000", nil); err != nil {
log.Fatal("Serving error.")
}
}
In response to the comments, I tried this solution with the same result:
package main
import (
"net/http"
"log"
"github.com/projectrepo/project/models"
"github.com/projectrepo/project/controllers"
"github.com/gorilla/mux"
)
func main () {
r := mux.NewRouter()
r.Handle("/v1", controllers.V1Router)
if err := http.ListenAndServe(":8000", r); err != nil {
log.Fatal("Serving error.")
}
}
Gorilla mux.Router is supposed to be used to create mapping between a set of predefined rules (e.g. host, path, protocol, scheme, etc...) and it's handler (http.Handler or http.HandlerFunc). Gorilla mux can be used to replace standard server mux. If you combine gorilla/mux with built in http server mux as your original question, i.e.
func main () {
http.Handle("/v1", controllers.V1Router)
if err := http.ListenAndServe(":8000", nil); err != nil {
log.Fatal("Serving error.")
}
}
what actually happen when a client access /v1 is controllers.V1Router will be called with request path /v1 passed to V1Router1. In the controllers.V1Router, you defined that / will be handled by BaseHandler. However, since incoming request path is /v1, it won't match to your routing table. If you want to define sub routing, you can do as follows (this is what I mean in first comment):
func main () {
r := mux.NewRouter()
v1 := r.PathPrefix("/v1").Subrouter()
controllers.RegisterHandlers(v1)
if err := http.ListenAndServe(":8000", r); err != nil {
log.Fatal("Serving error.")
}
}
Then in the controllers (base.controllers.go) define
//Register handlers and it's sub router
func RegisterHandlers(r *mux.Router) {
//base handler, i.e. /v1
r.StrictSlash(true)
r.HandleFunc("/", BaseHandler)
//example sub-router, i.e. /v1/example
ex := r.PathPrefix("/example").Subrouter()
ex.HandleFunc("/", ExampleHandler)
//other handlers...
}

Golang - User of package without selector

Please, I searched this a lot and after not been able to find, I am writing and not that I didn't try to search all over first. Couldn't get the right answer. I even tried to check Revel's function and couldn't get the answer from there as well.
When I run this program I get this error for line
./test.go:11: use of package http without selector
This error points at the line below where I have written
*http
inside the struct
Confusing part is that with test and dot I even get auto complete with VIM. So I don't know why is the error. Is it that it has to be somewhat like
*(net/http)
or something like that ?
package main
import (
"fmt"
"net/http"
)
type HandleHTTP struct {
*http
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Path is %s", r.URL.Path[1:])
}
func main() {
test := HandleHTTP{}
test.http.HandleFunc("/", handler)
test.http.ListenAndServe(":8080", nil)
}
If you want to have two or more instances serving from different ports you need to spin up two, or more, server. Would something like this, perhaps, work for you?
package main
import (
"fmt"
"net/http"
)
type HandleHTTP struct {
http *http.Server
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Path is %s", r.URL.Path[1:])
}
func main() {
mux1 := http.NewServeMux()
mux1.HandleFunc("/", handler)
test1 := HandleHTTP{http:&http.Server{Addr:":8081", Handler:mux1}}
mux2 := http.NewServeMux()
mux2.HandleFunc("/", handler)
test2 := HandleHTTP{http:&http.Server{Addr:":8082", Handler:mux2}}
// run the first one in a goroutine so that the second one is executed
go test1.http.ListenAndServe()
test2.http.ListenAndServe()
}

Profiling Go web application built with Gorilla's mux with net/http/pprof

I have a relatively big web application written in Go that uses Gorilla's mux for routing. I recently realised that my web application is quite slow and I would like to profile the web application.
After reading about it, it seems that net/http/pprof is what I need. But I can't make it run with mux; even in the case of the most trivial web application.
Does anyone knows how to make that work?
Here is an example of a trivial code that does not work (i.e. nothing is served at /debug).
package main
import (
"fmt"
"github.com/gorilla/mux"
"math"
"net/http"
)
import _ "net/http/pprof"
func SayHello(w http.ResponseWriter, r *http.Request) {
for i := 0; i < 1000000; i++ {
math.Pow(36, 89)
}
fmt.Fprint(w, "Hello!")
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/hello", SayHello)
http.ListenAndServe(":6060", r)
}
My preferred method for this is to just let net/http/pprof register itself to http.DefaultServeMux, and then pass all requests starting with /debug/pprof/ along:
package main
import (
"net/http"
_ "net/http/pprof"
"github.com/gorilla/mux"
)
func main() {
router := mux.NewRouter()
router.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux)
if err := http.ListenAndServe(":6060", router); err != nil {
panic(err)
}
}
I find that this approach is a lot more stable than one that depends on the implementation of a hidden initialization method, and also guarantees that you didn't miss anything.
user983716 - Thanks for your question and solution!
I was not able to use the links from the web index (http://[my-server]/debug/pprof), until I added a few lines to your solution, like so:
...
func AttachProfiler(router *mux.Router) {
router.HandleFunc("/debug/pprof/", pprof.Index)
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
// Manually add support for paths linked to by index page at /debug/pprof/
router.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
router.Handle("/debug/pprof/heap", pprof.Handler("heap"))
router.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
router.Handle("/debug/pprof/block", pprof.Handler("block"))
}
...
If anyone has the same problem, I hope this helps!
Sorry for that question. The answer is in the init() function of pprof. One just need to add 4 functions from pprof to the mux router. Here is the fixed code from above.
package main
import (
"fmt"
"github.com/gorilla/mux"
"math"
"net/http"
)
import "net/http/pprof"
func AttachProfiler(router *mux.Router) {
router.HandleFunc("/debug/pprof/", pprof.Index)
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
}
func SayHello(w http.ResponseWriter, r *http.Request) {
for i := 0; i < 1000000; i++ {
math.Pow(36, 89)
}
fmt.Fprint(w, "Hello!")
}
func main() {
r := mux.NewRouter()
AttachProfiler(r)
r.HandleFunc("/hello", SayHello)
http.ListenAndServe(":6060", r)
}
Previous examples not really work on my side.
To use pprof in an existing golang project with gorrila/mux, try to add :
...previous code
func main() {
r := mux.NewRouter()
r.HandleFunc("/hello", SayHello)
go func() {
log.Fatal(http.ListenAndServe(":6061", http.DefaultServeMux))
}()
http.ListenAndServe(":6060", r)
}
then go to http://localhost:6061/debug/pprof/
I did something else, I added another native http server on a different port and it just works out of the box
package main
import (
"fmt"
"log"
"net/http"
_ "net/http/pprof"
)
func main() {
go func() {
log.Println(http.ListenAndServe(":6060", nil))
}()
log.Fatalln(http.ListenAndServe(":8080", route.Handlers()))
}
Now the pprof endpoint is at :
http://localhost:6060/debug/pprof/ and the applcation is running on port :8080
Just so:
r := mux.NewRouter()
r.PathPrefix("/debug").Handler(http.DefaultServeMux)
Im using https://github.com/julienschmidt/httprouter but i just got this answer from google search.
That's what i did
router := httprouter.New()
router.Handler("GET", "/debug/pprof/profile", http.DefaultServeMux)
router.Handler("GET", "/debug/pprof/heap", http.DefaultServeMux)
I only need this two routes.
This answer is combine of #damien and #user983716 answers.
The following should work:
import (
"net/http"
_ "net/http/pprof"
)
myrouter.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux)

Resources