How does creating handlers works? - go

I am looking at this example
var name string
type helloWorldResponse struct {
Message string `json:"message"`
}
type helloWorldRequest struct {
Name string `json:"name"`
}
func main() {
port := 8080
handler := newValidationHandler(newHelloWorldHandler())
http.Handle("/helloworld", handler)
log.Printf("Server starting on port %v\n", port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil))
}
type validationHandler struct {
next http.Handler
}
func newValidationHandler(next http.Handler) http.Handler {
return validationHandler{next: next}
}
func (h validationHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
var request helloWorldRequest
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&request)
if err != nil {
http.Error(rw, "Bad request", http.StatusBadRequest)
return
}
name = request.Name
h.next.ServeHTTP(rw, r)
}
type helloWorldHandler struct{}
func newHelloWorldHandler() http.Handler {
return helloWorldHandler{}
}
func (h helloWorldHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
response := helloWorldResponse{Message: "Hello " + name}
encoder := json.NewEncoder(rw)
encoder.Encode(response)
}
The author of the code explained that we are
going to be chaining handlers together, the first handler, which is our validation handler,
needs to have a reference to the next in the chain as it has the responsibility for calling
ServeHTTP or returning a response. I am newbe to Go and I do not understand this line
return validationHandler{next: next}
Which data structure next:next represents?

type validationHandler struct {
next http.Handler // 1
}
func newValidationHandler(next /* 2 */ http.Handler) http.Handler {
return validationHandler{next: next}
// 1 2
}
next number 1 is a field from validationHandler struct (a few lines above). And the other next is method's parameter (from the signature). All in all, this simply sets a field in a struct. No magic.
Which data structure next:next represents?
Not a data structure. It is struct initialization syntax. See more examples here: https://gobyexample.com/structs

Related

log http.ResponseWriter content

Premise: I've found a similar issue but not working in my case, so please do not mark this as a duplicate.
I've a HTTP server in Go and I've created a middleware to log the request, the response time and I would like to log the response too.
I've used httputil.DumpRequest in a function called HTTPRequest under the package log.
How can I correctly get the response body and status and headers from the w http.ResponseWriter and log them together with the other data?
My ISSUE is: I would like to intercept the Response Headers, Status and Body and to log the together with the Request and Response Time
Here's the code:
log "core/logger"
...
func RequestLoggerMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
defer func() {
log.Info(
fmt.Sprintf(
"[Request: %s] [Execution time: %v] [Response: %s]",
log.HTTPRequest(r),
time.Since(start),
// RESPONSE DATA HERE !!!!!!!
))
}()
next.ServeHTTP(w, r)
})
}
Thanks, #Sivachandran for the response. It was almost perfect, only it didn't implement the http.ResponseWriter because of the pointers.
For the sake of completeness, I post here the correct solution code, because it's not easy to find any documentation on it, even if this question has been given a negative score.
Stackoverflow is a good place to exchange questions and this, in my opinion, was a very good and difficult question, either for a middle lever Golang programmer, so it didn't deserve a negative score at all!
That's the solution, enjoy:
// RequestLoggerMiddleware is the middleware layer to log all the HTTP requests
func RequestLoggerMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rww := NewResponseWriterWrapper(w)
w.Header()
defer func() {
log.Info(
fmt.Sprintf(
"[Request: %s] [Execution time: %v] [Response: %s]",
log.HTTPRequest(r),
time.Since(start),
rww.String(),
))
}()
next.ServeHTTP(rww, r)
})
}
// ResponseWriterWrapper struct is used to log the response
type ResponseWriterWrapper struct {
w *http.ResponseWriter
body *bytes.Buffer
statusCode *int
}
// NewResponseWriterWrapper static function creates a wrapper for the http.ResponseWriter
func NewResponseWriterWrapper(w http.ResponseWriter) ResponseWriterWrapper {
var buf bytes.Buffer
var statusCode int = 200
return ResponseWriterWrapper{
w: &w,
body: &buf,
statusCode: &statusCode,
}
}
func (rww ResponseWriterWrapper) Write(buf []byte) (int, error) {
rww.body.Write(buf)
return (*rww.w).Write(buf)
}
// Header function overwrites the http.ResponseWriter Header() function
func (rww ResponseWriterWrapper) Header() http.Header {
return (*rww.w).Header()
}
// WriteHeader function overwrites the http.ResponseWriter WriteHeader() function
func (rww ResponseWriterWrapper) WriteHeader(statusCode int) {
(*rww.statusCode) = statusCode
(*rww.w).WriteHeader(statusCode)
}
func (rww ResponseWriterWrapper) String() string {
var buf bytes.Buffer
buf.WriteString("Response:")
buf.WriteString("Headers:")
for k, v := range (*rww.w).Header() {
buf.WriteString(fmt.Sprintf("%s: %v", k, v))
}
buf.WriteString(fmt.Sprintf(" Status Code: %d", *(rww.statusCode)))
buf.WriteString("Body")
buf.WriteString(rww.body.String())
return buf.String()
}
You need to wrap the ResponseWriter to capture the response data.
type ResponseWriterWrapper struct {
w http.ResponseWriter
body bytes.Buffer
statusCode int
}
func (i *ResponseWriterWrapper) Write(buf []byte) (int, error) {
i.body.Write(buf)
return i.w.Write(buf)
}
func (i *ResponseWriterWrapper) WriteHeader(statusCode int) {
i.statusCode = statusCode
i.w.WriteHeader(statusCode)
}
func (i *ResponseWriterWrapper) String() {
var buf bytes.Buffer
buf.WriteString("Response:")
buf.WriteString("Headers:")
for k, v := range i.w.Header() {
buf.WriteString(fmt.Sprintf("%s: %v", k, v))
}
buf.WriteString(fmt.Sprintf("Status Code: %d", i.statusCode))
buf.WriteString("Body")
buf.WriteString(i.body.String())
}
Pass the wrapper to ServeHTTP and log captured response data.
func RequestLoggerMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rww := ResponseWriterWrapper{ w: w }
defer func() {
log.Info(
fmt.Sprintf(
"[Request: %s] [Execution time: %v] [Response: %s]",
log.HTTPRequest(r),
time.Since(start),
log.Info(rww.String())
))
}()
next.ServeHTTP(rww, r)
})
}

GzipResponseWriter field declaration

I am looking at this type
type GzipResponseWriter struct {
gw *gzip.Writer
http.ResponseWriter
}
And functions that will implement it
func (w GzipResponseWriter) Write(b []byte) (int, error) {
if _, ok := w.Header()["Content-Type"]; !ok {
// If content type is not set, infer it from the uncompressed body.
w.Header().Set("Content-Type", http.DetectContentType(b))
}
return w.gw.Write(b)
}
func (w GzipResponseWriter) Flush() {
w.gw.Flush()
if fw, ok := w.ResponseWriter.(http.Flusher); ok {
fw.Flush()
}
}
Does the http.ResponseWriter relate to the second field?
Why not
gw1 http.ResponseWriter?

impossible type switch case: cannot have dynamic type

Have following struct:
package dto
type CapacityResponse struct{
Val int
Err error
TransactionID string
}
func (r *CapacityResponse) GetError() (error) {
return r.Err
}
func (r *CapacityResponse) SetError(err error) {
r.Err = err
}
func (r *CapacityResponse) Read() interface{} {
return r.Val
}
func (r *CapacityResponse) GetTransactionId() string {
return r.TransactionID
}
It's interface:
package dto
type Responder interface {
Read() (interface{})
GetError() (error)
SetError(error)
GetTransactionId() (string)
}
And following logic:
func handler(w http.ResponseWriter, r *http.Request) {
cr := request2CacheRequest(r)
responseChan := make(chan dto.Responder)
go func() {
responder := processReq(cr)
responseChan <- responder
}()
go func() {
for r := range responseChan {
if (r.GetTransactionId() == cr.TransactionID) {
switch r.(type) {
//case dto.KeysResponse:
//case dto.GetResponse:
//case dto.RemoveResponse:
//case dto.SetResponse:
case dto.CapacityResponse:
if i, ok := r.Read().(int); ok {
fmt.Fprintf(w, fmt.Sprintf("{'capasity': %d, 'err': %s}", i, r.GetError()))
}
}
}
}
}()
}
I am getting exception:
impossible type switch case: r (type dto.Responder) cannot have dynamic type dto.CapacityResponse (missing GetError method)
Could you, please, help me to understand what is wrong here?
The error message is saying that a dto.Responder value cannot contain a dto.CapacityResponse because dto.CapacityResponse is missing one of the dto.Responder methods (GetError).
The pointer type implements the interface. Change the case to:
case *dto.CapacityResponse:
You have this error because dto.CapacityResponse type is different from *dto.CapacityResponse type.
Because you are using local variable r of interface type dto.Responder the only concrete types you can use in case statements are those that implement this interface and dto.CapacityResponse isn't one of them, because it is not a pointer type and you have declared receivers as pointers for dto.CapacityResponse. Please take a look on playground example

Golang handlers handling different types

These are AppHandlers from a pattern I found online while researching gorilla/mux. They part of a struct that satisfies http.Handler. If you notice, the following two blocks are exactly the same. Effectively, they could be passed the 'variant' ("flow" or "process") as a string.
func CreateFlow(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
highest, code, err := a.Create("flow", r)
if code != 200 || err != nil {
return code, err
}
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(struct {
Highest int `json:"id"`
}{highest})
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
func CreateProcess(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
highest, code, err := a.Create("process", r)
if code != 200 || err != nil {
return code, err
}
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(struct {
Highest int `json:"id"`
}{highest})
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
However, the following two blocks not only need the string, but they need a variable of the associated type ("Flow" and "Process") to successfully Unmarshal the hit I get from ElasticSearch. Other than that, they are Identical code.
func GetFlow(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
hit, code, err := a.GetByID("flow", mux.Vars(r)["id"], r)
if code != 200 {
return code, err
}
var flow Flow
err = json.Unmarshal(*hit.Source, &flow)
if err != nil {
return 500, err
}
flow.ESID = hit.Id
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(flow)
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
func GetProcess(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
hit, code, err := a.GetByID("process", mux.Vars(r)["id"], r)
if code != 200 {
return code, err
}
var process Process
err = json.Unmarshal(*hit.Source, &process)
if err != nil {
return 500, err
}
process.ESID = hit.Id
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(process)
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
I am not sure how to generalize this behavior in golang when there is a declared type involved. These handlers are all in the same package too, as I think that they are all accomplishing a similar task. I am very clearly repeating myself in code but I need advice on how I can improve. I've gone past "a little copying is better than a little dependency." but I am afraid because "reflection is never clear".
Here is an example of the declaration in main using one of these functions.
api.Handle("/flow/{id:[0-9]+}", handlers.AppHandler{context, handlers.GetFlow}).Methods("GET")
You can do it by passing in an exemplar of the necessary type, the same way that Unmarshal does it:
func GetFlow(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
return GetThing(a,w,r,"flow",new(Flow))
}
func GetProcess(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
return GetThing(a,w,r,"process",new(Process))
}
func GetThing(a *AppContext, w http.ResponseWriter, r *http.Request, t string, ob Elastible{}) (int, error) {
hit, code, err := a.GetByID(t, mux.Vars(r)["id"], r)
if code != 200 {
return code, err
}
err = json.Unmarshal(*hit.Source, ob)
if err != nil {
return 500, err
}
ob.SetESID(hit.Id)
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(ob)
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
type Elastible interface {
SetESID(id ESIDType) // whatever type ESID is, not clear from example
}
func (f *Flow) SetESID(id ESIDType) {
f.ESID = id
}
This code is untested (because I don't have your struct defs or other dependent code) but I hope it gets the idea across.
Alright, I propose a solution that will give you the maximum code reuse and minimum code copying. This, in my opinion, is by far the most generic solution. We will also take into account the answer given by https://stackoverflow.com/users/7426/adrian to complete the solution. You only have to define a single function which will be a higher order function CreateHandler which will return a function of the following signature:
func(*AppContext, http.ResponseWriter, http.Request) (int, error).
This signature is the actual signature of the handler that is to be used as a mux end point. The solution involves defining a Handler type which is a struct having three fields:
• handlerType: Think of it as an enum having either a value of "CREATE" or "GET". This will decide which among the two blocks of code that you pasted in your question should we use.
• handlerActionName: This will tell the "CREATE" or "GET" which Elastible to use. Value should either be "flow" or "process".
• elastible: This will the Interface type Elastible that will have the SetESID function. We will use this to send our Flow or Process types to our Handler. Thus both Flow and Process should satisfy our interface.
This will make the solution even more generic and will only calling handler.elastible.SetESID() and we will have inserted the ESID irrespective of that fact the underlying type in 'elastible' can either be 'Flow' or a 'Process'
I also define a sendResponse(response interface{}) function that we will resuse to send the response. It acquires w http.ResponseWriter using closure. response can thus be anything, a
struct {
Highest int `json:"id"`
}{highest}
or a Flow or a Process. This will make this function generic too.
The complete solution would now be.
// This is the type that will be used to build our handlers.
type Handler struct {
handlerType string // Can be "CREATE" or "GET"
handlerActionName string // Can be "flow" or "process"
elastible Elastible // Can be *Flow or *Process
}
// Your ESID Type.
type ESIDType string
// Solution proposed by https://stackoverflow.com/users/7426/adrian.
type Elastible interface {
SetESID(id ESIDType)
}
// Make the Flow and Process pointers implement the Elastible interface.
func (flow *Flow) SetESID(id ESIDType) {
flow.ESID = id
}
func (process *Process) SetESID(id ESIDType) {
process.ESID = id
}
// Create a Higher Order Function which will return the actual handler.
func CreateHandler(handler Handler) func(*AppContext, http.ResponseWriter, http.Request) (int, error) {
return func(a *AppContext, w http.ResponseWriter, r http.Request) (int, error) {
// Define a sendResponse function so that we may not need to copy paste it later.
// It captures w using closure and takes an interface argument that we use to call .Encode() with.
sendResponse := func(response interface{}) (int, error) {
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(response)
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
// Define these variables beforehand since we'll be using them
// in both the if and else block. Not necessary really.
var code int
var err error
// Check the handlerType. Is it create or get?
if handler.handlerType == "CREATE" {
var highest int
// Creates the thing using handler.handlerActionName which may be "flow" or "process"
highest, code, err = a.Create(handler.handlerActionName, r)
if code != 200 || err != nil {
return code, err
}
// Send the response using the above defined function and return.
return sendResponse(struct {
Highest int `json:"id"`
}{highest})
} else {
// This is GET handlerType.
var hit HitType
// Get the hit using again the handler.handlerActionName which may be "flow" or "process"
hit, code, err = a.GetByID(handler.handlerActionName, mux.Vars(r)["id"], r)
if code != 200 || err != nil {
return code, err
}
// Do the un-marshalling.
err = json.Unmarshal(*hit.Source, ob)
if err != nil {
return 500, err
}
// We have set the handler.elastible to be an interface type
// which will have the SetESID function that will set the ESID in the
// underlying type that will be passed on runtime.
// So the ESID will be set for both the Flow and the Process types.
// This interface idea was given inside an earlier answer by
// https://stackoverflow.com/users/7426/adrian
handler.elastible.SetESID(hit.id)
return sendResponse(handler.elastible)
}
}
}
And you would setup your mux end points using the following code.
// This was your first function. "CreateFlow"
api.Handle("/createFlow/{id:[0-9]+}", handlers.AppHandler{
context, CreateHandler(Handler{
elastible: &Flow{},
handlerActionName: "flow",
handlerType: "CREATE",
}),
}).Methods("GET")
// This was your second function. "CreateProcess"
api.Handle("/createProcess/{id:[0-9]+}", handlers.AppHandler{
context, CreateHandler(Handler{
elastible: &Process{},
handlerActionName: "process",
handlerType: "CREATE",
}),
}).Methods("GET")
// This was your third function. "GetFlow"
api.Handle("/getFlow/{id:[0-9]+}", handlers.AppHandler{
context, CreateHandler(Handler{
elastible: &Flow{},
handlerActionName: "flow",
handlerType: "GET",
}),
}).Methods("GET")
// This was your fourth function. "GetProcess"
api.Handle("/getProcess/{id:[0-9]+}", handlers.AppHandler{
context, CreateHandler(Handler{
elastible: &Process{},
handlerActionName: "process",
handlerType: "GET",
}),
}).Methods("GET")
Hope it helps!

Showing custom 404 error page with standard http package

Assuming that we have:
http.HandleFunc("/smth", smthPage)
http.HandleFunc("/", homePage)
User sees a plain "404 page not found" when they try a wrong URL. How can I return a custom page for that case?
Update concerning gorilla/mux
Accepted answer is ok for those using pure net/http package.
If you use gorilla/mux you should use something like this:
func main() {
r := mux.NewRouter()
r.NotFoundHandler = http.HandlerFunc(notFound)
}
And implement func notFound(w http.ResponseWriter, r *http.Request) as you want.
I usually do this:
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", homeHandler)
http.HandleFunc("/smth/", smthHandler)
http.ListenAndServe(":12345", nil)
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
errorHandler(w, r, http.StatusNotFound)
return
}
fmt.Fprint(w, "welcome home")
}
func smthHandler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/smth/" {
errorHandler(w, r, http.StatusNotFound)
return
}
fmt.Fprint(w, "welcome smth")
}
func errorHandler(w http.ResponseWriter, r *http.Request, status int) {
w.WriteHeader(status)
if status == http.StatusNotFound {
fmt.Fprint(w, "custom 404")
}
}
Here I've simplified the code to only show custom 404, but I actually do more with this setup: I handle all the HTTP errors with errorHandler, in which I log useful information and send email to myself.
Following is the approach I choose. It is based on a code snippet which I cannot acknowledge since I lost the browser bookmark.
Sample code : (I put it in my main package)
type hijack404 struct {
http.ResponseWriter
R *http.Request
Handle404 func (w http.ResponseWriter, r *http.Request) bool
}
func (h *hijack404) WriteHeader(code int) {
if 404 == code && h.Handle404(h.ResponseWriter, h.R) {
panic(h)
}
h.ResponseWriter.WriteHeader(code)
}
func Handle404(handler http.Handler, handle404 func (w http.ResponseWriter, r *http.Request) bool) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
hijack := &hijack404{ ResponseWriter:w, R: r, Handle404: handle404 }
defer func() {
if p:=recover(); p!=nil {
if p==hijack {
return
}
panic(p)
}
}()
handler.ServeHTTP(hijack, r)
})
}
func fire404(res http.ResponseWriter, req *http.Request) bool{
fmt.Fprintf(res, "File not found. Please check to see if your URL is correct.");
return true;
}
func main(){
handler_statics := http.StripPrefix("/static/", http.FileServer(http.Dir("/Path_To_My_Static_Files")));
var v_blessed_handler_statics http.Handler = Handle404(handler_statics, fire404);
http.Handle("/static/", v_blessed_handler_statics);
// add other handlers using http.Handle() as necessary
if err := http.ListenAndServe(":8080", nil); err != nil{
log.Fatal("ListenAndServe: ", err);
}
}
Please customize the func fire404 to output your own version of message for error 404.
If you happen to be using Gorilla Mux, you may wish to replace the main function with below :
func main(){
handler_statics := http.StripPrefix("/static/", http.FileServer(http.Dir("/Path_To_My_Static_Files")));
var v_blessed_handler_statics http.Handler = Handle404(handler_statics, fire404);
r := mux.NewRouter();
r.PathPrefix("/static/").Handler(v_blessed_handler_statics);
// add other handlers with r.HandleFunc() if necessary...
http.Handle("/", r);
log.Fatal(http.ListenAndServe(":8080", nil));
}
Please kindly correct the code if it is wrong, since I am only a newbie to Go. Thanks.
Ancient thread, but I just made something to intercept http.ResponseWriter, might be relevant here.
package main
//GAE POC originally inspired by https://thornelabs.net/2017/03/08/use-google-app-engine-and-golang-to-host-a-static-website-with-same-domain-redirects.html
import (
"net/http"
)
func init() {
http.HandleFunc("/", handler)
}
// HeaderWriter is a wrapper around http.ResponseWriter which manipulates headers/content based on upstream response
type HeaderWriter struct {
original http.ResponseWriter
done bool
}
func (hw *HeaderWriter) Header() http.Header {
return hw.original.Header()
}
func (hw *HeaderWriter) Write(b []byte) (int, error) {
if hw.done {
//Silently let caller think they are succeeding in sending their boring 404...
return len(b), nil
}
return hw.original.Write(b)
}
func (hw *HeaderWriter) WriteHeader(s int) {
if hw.done {
//Hmm... I don't think this is needed...
return
}
if s < 400 {
//Set CC header when status is < 400...
//TODO: Use diff header if static extensions
hw.original.Header().Set("Cache-Control", "max-age=60, s-maxage=2592000, public")
}
hw.original.WriteHeader(s)
if s == 404 {
hw.done = true
hw.original.Write([]byte("This be custom 404..."))
}
}
func handler(w http.ResponseWriter, r *http.Request) {
urls := map[string]string{
"/example-post-1.html": "https://example.com/post/example-post-1.html",
"/example-post-2.html": "https://example.com/post/example-post-2.html",
"/example-post-3.html": "https://example.com/post/example-post-3.html",
}
w.Header().Set("Strict-Transport-Security", "max-age=15768000")
//TODO: Put own logic
if value, ok := urls[r.URL.Path]; ok {
http.Redirect(&HeaderWriter{original: w}, r, value, 301)
} else {
http.ServeFile(&HeaderWriter{original: w}, r, "static/"+r.URL.Path)
}
}
i think the clean way is this:
func main() {
http.HandleFunc("/calculator", calculatorHandler)
http.HandleFunc("/history", historyHandler)
http.HandleFunc("/", notFoundHandler)
log.Fatal(http.ListenAndServe(":80", nil))
}
if the address is not /calulator or /history, then it handles notFoundHandler function.
Maybe I'm wrong, but I just checked the sources: http://golang.org/src/pkg/net/http/server.go
It seems like specifying custom NotFound() function is hardly possible: NotFoundHandler() returns a hardcoded function called NotFound().
Probably, you should submit an issue on this.
As a workaround, you can use your "/" handler, which is a fallback if no other handlers were found (as it is the shortest one). So, check is page exists in that handler and return a custom 404 error.
You just need to create your own notFound handler and register it with HandleFunc for the path that you don't handle.
If you want the most control over your routing logic you will need to use a custom server and custom handler type of your own.
http://golang.org/pkg/net/http/#Handler
http://golang.org/pkg/net/http/#Server
This allows you to implement more complex routing logic than the HandleFunc will allow you to do.
you can define
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
if request.URL.Path != "/" {
writer.WriteHeader(404)
writer.Write([]byte(`not found, da xiong dei !!!`))
return
}
})
when access not found resource, it will execute to http.HandleFunc("/", xxx)
You can simply use something like:
func Handle404(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "404 error\n")
}
func main(){
http.HandleFunc("/", routes.Handle404)
}
If you need to get the standard one, just write:
func main(){
http.HandleFunc("/", http.NotFound)
}
And you'll get:
404 page not found

Resources