Skip to content

Commit

Permalink
add example and an interface for using the muxie writer with a custom…
Browse files Browse the repository at this point in the history
… response writer one

relative to: #10
  • Loading branch information
kataras committed Feb 6, 2021
1 parent 29e8872 commit 1dad712
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 8 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<div align="center">
<!-- Release -->
<a href="https://github.com/kataras/muxie/releases">
<img src="https://img.shields.io/badge/release%20-v1.1.1-0077b3.svg?style=flat-squaree"
<img src="https://img.shields.io/badge/release%20-v1.1.2-0077b3.svg?style=flat-squaree"
alt="Release/stability" />
</a>
<!-- Godocs -->
Expand Down
61 changes: 61 additions & 0 deletions _examples/13_custom_responsewriter/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package main

import (
"fmt"
"net/http"
"strconv"
"time"

"github.com/kataras/muxie"
)

func main() {
mux := muxie.NewMux()
mux.Use(RequestTime)
mux.HandleFunc("/profile/:name", profileHandler)
fmt.Println(`Server started at http://localhost:8080
Open your browser or any other HTTP Client and navigate to:
http://localhost:8080/profile/yourname`)

http.ListenAndServe(":8080", mux)
}

func profileHandler(w http.ResponseWriter, r *http.Request) {
name := muxie.GetParam(w, "name")
fmt.Fprintf(w, "Hello, %s!", name)
}

type responseWriterWithTimer struct {
// muxie.ParamStore
// http.ResponseWriter
// OR
*muxie.Writer
// OR/and implement the ParamStore interface by your own if you want
// to customize the way the parameters are stored and retrieved.
isHeaderWritten bool
start time.Time
}

// RequestTime is a middleware which modifies the response writer to use the `responseWriterWithTimer`.
// Look at: https://github.com/kataras/muxie/issues/10 too.
func RequestTime(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// w.(muxie.ParamStore), w OR cast it directly to the *muxie.Writer:
next.ServeHTTP(&responseWriterWithTimer{w.(*muxie.Writer), false, time.Now()}, r)
})
}

func (w *responseWriterWithTimer) WriteHeader(statusCode int) {
elapsed := time.Since(w.start)
w.Header().Set("X-Response-Time", strconv.FormatInt(elapsed.Nanoseconds(), 10))

w.ResponseWriter.WriteHeader(statusCode)
w.isHeaderWritten = true
}

func (w *responseWriterWithTimer) Write(b []byte) (int, error) {
if !w.isHeaderWritten {
w.WriteHeader(200)
}
return w.ResponseWriter.Write(b)
}
2 changes: 1 addition & 1 deletion doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Source code and other details for the project are available at GitHub:
Current Version
1.1.1
1.1.2
Installation
Expand Down
24 changes: 18 additions & 6 deletions params_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,37 @@ package muxie

import "net/http"

// ParamStore should be completed by http.ResponseWriter to support dynamic path parameters.
// See the `Writer` type for more.
// This interface can be implemented by custom response writers.
// Example of implementation: to change where and how the parameters are stored and retrieved.
type ParamStore interface {
Set(key string, value string)
Get(key string) string
GetAll() []ParamEntry
}

// GetParam returns the path parameter value based on its key, i.e
// "/hello/:name", the parameter key is the "name".
// For example if a route with pattern of "/hello/:name" is inserted to the `Trie` or handlded by the `Mux`
// and the path "/hello/kataras" is requested through the `Mux#ServeHTTP -> Trie#Search`
// then the `GetParam("name")` will return the value of "kataras".
// If not associated value with that key is found then it will return an empty string.
//
// The function will do its job only if the given "w" http.ResponseWriter interface is an `ResponseWriter`.
// The function will do its job only if the given "w" http.ResponseWriter interface is a `ParamStore`.
func GetParam(w http.ResponseWriter, key string) string {
if store, ok := w.(*Writer); ok {
if store, ok := w.(ParamStore); ok {
return store.Get(key)
}

return ""
}

// GetParams returns all the available parameters based on the "w" http.ResponseWriter which should be a ResponseWriter.
// GetParams returns all the available parameters based on the "w" http.ResponseWriter which should be a ParamStore.
//
// The function will do its job only if the given "w" http.ResponseWriter interface is an `ResponseWriter`.
// The function will do its job only if the given "w" http.ResponseWriter interface is a `ParamStore`.
func GetParams(w http.ResponseWriter) []ParamEntry {
if store, ok := w.(*Writer); ok {
if store, ok := w.(ParamStore); ok {
return store.GetAll()
}

Expand All @@ -33,7 +43,7 @@ func GetParams(w http.ResponseWriter) []ParamEntry {
// This is not commonly used by the end-developers,
// unless sharing values(string messages only) between handlers is absolutely necessary.
func SetParam(w http.ResponseWriter, key, value string) bool {
if store, ok := w.(*Writer); ok {
if store, ok := w.(ParamStore); ok {
store.Set(key, value)
return true
}
Expand All @@ -55,6 +65,8 @@ type Writer struct {
params []ParamEntry
}

var _ ParamStore = (*Writer)(nil)

// Set implements the `ParamsSetter` which `Trie#Search` needs to store the parameters, if any.
// These are decoupled because end-developers may want to use the trie to design a new Mux of their own
// or to store different kind of data inside it.
Expand Down

0 comments on commit 1dad712

Please sign in to comment.