I am trying to pass string to handler in given example.
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
Here is what i tried but it throws an error as it expects regular number of arguments:
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request, s *string) {
fmt.Fprintf(w, "Hi there, I love %s!", *s)
}
func main() {
files := "bar"
http.HandleFunc("/", handler(&files))
http.ListenAndServe(":8080", nil)
}
I'm a little unclear on what you're trying to do, but based off what you said, why not try to encapsulate the data you want to pass in like this:
package main
import (
"fmt"
"net/http"
)
type FilesHandler struct {
Files string
}
func (fh *FilesHandler) handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there, I love %s!", fh.Files)
}
func main() {
myFilesHandler := &FilesHandler{Files: "bar"}
http.HandleFunc("/", myFilesHandler.handler)
http.ListenAndServe(":8080", nil)
}
This provides a little more granular control of what you make available to your Handler.
There are lots of options here, you could:
Use a closure to set state for the enclosed handler
Use a method on a struct for the handler, and set global state there
Use the request context to store a value, then get it out
Use a package global to store the value
Write your own router with a new signature (not as complex as it sounds, but probably not a good idea)
Write a helper function to do things like extract params from the url
It depends what s is really - is it a constant, is it based on some state, does it belong in a separate package?
One of ways is to store data in global variable:
package main
import (
"fmt"
"net/http"
)
var replies map[string]string
func handler1(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
question := r.FormValue("question")
var answer string
var ok bool
if answer, ok = replies[question]; !ok {
answer = "I have no answer for this"
}
fmt.Fprintf(w, "Hi there, I love %s! My answer is: %s", question, answer)
}
func main() {
//files := "bar"
replies = map[string]string{
"UK": "London",
"FR": "Paris",
}
http.HandleFunc("/", handler1)
http.ListenAndServe(":8080", nil)
}
Here for brevity I've commented out files and put data as is into the map. You may read the file and put them there.
Usage with CURL:
$ curl -X POST 127.0.0.1:8080/ -d "question=FR"
Hi there, I love FR! My answer is: Paris
$ curl -X POST 127.0.0.1:8080/ -d "question=US"
Hi there, I love US! My answer is: I have no answer for this
Related
I'm using GO for the first time and am setting up a little example API. In trying to return a JSON object from a struct that I made, I get this error when I add a struct tag to my fields:
"field tag must be a string" and "invalid character literal (more than one character)".
Here's my code breakdown. What am I missing here?
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
func main() {
router := mux.NewRouter()
router.HandleFunc("/demo/v1/version", getVersion).Methods("GET")
log.Fatal(http.ListenAndServe(":8080", router))
}
func getVersion(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
version := Version{ID: "demo", Version: "1.0.0", Sha: "some hash...."}
var myJSON, err = json.Marshal(version)
json.NewEncoder(w).Encode(myJSON)
}
type Version struct {
//ERRORS on these 3 lines:
ID string 'json:"id"'
Version string 'json:"version, omitempty"'
Sha string 'json:"sha"'
}
You need to encapsulate your struct tags with back quotes instead of using single quotes to create raw string literals which can allow for the inclusion of additional data in the tag field.
This post gives a good explanation of tags, how they are constructed properly and should serve as a good reference for further explanation if needed.
Working code here:
package main
import (
"encoding/json"
"log"
"net/http"
"github.com/gorilla/mux"
)
type Version struct {
ID string `json:"id"`
Version string `json:"version, omitempty"`
Sha string `json:"sha"`
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/demo/v1/version", getVersion).Methods("GET")
log.Fatal(http.ListenAndServe(":8080", router))
}
func getVersion(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
version := Version{ID: "demo", Version: "1.0.0", Sha: "some hash...."}
var myJSON, err = json.Marshal(version)
if err != nil {
// handle error
}
json.NewEncoder(w).Encode(myJSON)
}
I created in the example at the bottom a little server which is running on port 3000. You can access it over "htto://localhost:3000/time". The whole Request is covered with two middlewares. First "cancelHandler" and Second "otherHandler" is called - which is responding with some dummy data after 4 seconds.
To my problem: When i request the page in a browser and then cancel the request (before the 4sec). The server is still handling the goroutine/request in the background. I spent already hours to find a solution on google but i can just not wrap my head around the context. (context.WithCancel()) I get that i have to create a chan and listen to it but how does this work with the requests. thats already a goroutine, do i have to create another goroutine in the request/goroutine? Also another question is, should i really use Context for that or is there an easier solution with the cancelNotifier?
Maybe someone can describe it for me and others which maybe have the same understanding problem.
Solution should be that the cancel Handler is stopping the goroutine/request, when the browser cancels the request.
Thank you very much for your time!
package main
import (
"log"
"net/http"
"time"
"fmt"
)
func otherHandler(format string) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
time.Sleep(time.Duration(4)*time.Second)
tm := time.Now().Format(format)
w.Write([]byte("The time is: " + tm))
fmt.Println("response:", "The time is: "+tm)
}
return http.HandlerFunc(fn)
}
func cancelHandler(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
fmt.Println("start: called cancelHandler")
h.ServeHTTP(w, r)
fmt.Println("end: called cancelHandler")
}
return http.HandlerFunc(fn)
}
func main() {
mux := http.NewServeMux()
th := otherHandler(time.RFC1123)
mux.Handle("/time", cancelHandler(th))
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
The only way to "stop" a function is to return from it. Thus, time.Sleep cannot be interrupted. Use a select statement instead:
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
http.ListenAndServe(":3000", otherHandler(time.RFC1123))
}
func otherHandler(format string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
select {
case <-time.After(4 * time.Second):
// time's up
case <-r.Context().Done():
// client gave up
return
}
tm := time.Now().Format(format)
w.Write([]byte("The time is: " + tm))
fmt.Println("response:", "The time is: "+tm)
}
}
In general, check the request context (or one that is derived from it) in strategic places. If the context is canceled, don't proceed any further and return.
The code
package main
import (
"fmt"
"log"
"net/http"
"github.com/goji/httpauth"
)
func rootHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
data := "TEST"
w.Header().Set("Content-Length", fmt.Sprint(len(data)))
fmt.Fprint(w, string(data))
}
func main() {
r:=http.HandlerFunc(rootHandler)
http.HandleFunc("/", httpauth.SimpleBasicAuth("dave", "somepassword")(r))
log.Fatal(http.ListenAndServe(":8080", nil))
}
returns
:!go build test.go
# command-line-arguments
./test.go:23:71: cannot use httpauth.SimpleBasicAuth("dave", "somepassword")(r) (type http.Handler) as type func(http.ResponseWriter, *http.Request) in argument to http.HandleFunc
What am I doing wrong? I'm new to Go and don't understand why the example here is incomplete and doesn't include a YourHandler function.
I figured it out after much banging my head against the proverbial wall.
Use http.Handle, not http.HandleFunc! :)
So with the main function as
func main() {
r:=http.HandlerFunc(rootHandler)
http.Handle("/", httpauth.SimpleBasicAuth("dave", "somepassword")(r))
log.Fatal(http.ListenAndServe(":8080", nil))
}
You have a complete working example of httpauth with net/http!
This answer has a really great overview of the inner workings as well.
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()
}
Is it possible to not copy paste expression commonHanlder(handler1), commonHanlder(handler2) ... commonHanlder(handlerN) in this code:
rtr.HandleFunc("/", commonHanlder(handler1)).Methods("GET")
rtr.HandleFunc("/page2", commonHanlder(handler2)).Methods("GET")
and set it in one place like
http.ListenAndServe(":3000", commonHanlder(http.DefaultServeMux))
But this variant is not working and gives two errors on compile:
./goRelicAndMux.go:20: cannot use http.DefaultServeMux (type *http.ServeMux) as type gorelic.tHTTPHandlerFunc in argument to commonHanlder
./goRelicAndMux.go:20: cannot use commonHanlder(http.DefaultServeMux) (type gorelic.tHTTPHandlerFunc) as type http.Handler in argument to http.ListenAndServe:
gorelic.tHTTPHandlerFunc does not implement http.Handler (missing ServeHTTP method)
The full code:
package main
import (
"github.com/gorilla/mux"
"github.com/yvasiyarov/gorelic"
"log"
"net/http"
)
func main() {
initNewRelic()
rtr := mux.NewRouter()
var commonHanlder = agent.WrapHTTPHandlerFunc
rtr.HandleFunc("/", commonHanlder(handler1)).Methods("GET")
rtr.HandleFunc("/page2", commonHanlder(handler2)).Methods("GET")
http.Handle("/", rtr)
log.Println("Listening...")
http.ListenAndServe(":3000", http.DefaultServeMux)
}
func handler1(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("mainPage"))
}
func handler2(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("page 2"))
}
var agent *gorelic.Agent
func initNewRelic() {
agent = gorelic.NewAgent()
agent.Verbose = true
agent.NewrelicName = "test"
agent.NewrelicLicense = "new relic key"
agent.Run()
}
It seems like you want to call commonHandler on the root of your application and have it work for all. Since you are using mux, just wrap the mux router once.
func main() {
initNewRelic()
rtr := mux.NewRouter()
var commonHandler = agent.WrapHTTPHandler
rtr.HandleFunc("/", handler1).Methods("GET")
rtr.HandleFunc("/page2", handler2).Methods("GET")
http.Handle("/", commonHandler(rtr))
log.Println("Listening...")
http.ListenAndServe(":3000", nil)
}
I also removed the http.DefaultServeMux reference in ListenAndServe since passing nil will automatically use the default.