Skip to content

delicb/mezvaro

Repository files navigation

Mezvaro Build StatusCoverageGoDoc

Middleware management library for Golang.

Why?

There are bunch of great libraries for Golang when it comes to middleware managemant. So, why create another one? Most of the libraries are constrained by respecting http.Handler interface. This is fine, but I wanted to have full power of context and I do not mind having different signature for my handlers then one defined in standard library.

Having context stored in global map somewhere defeates the purpose of having context at all IMHO and storing it in ResponseWriter wrapper is just bad workaround since context logically does not belong in response object of any kind, so aproach taken by Mezvaro is to provide context object as parameter to every handler and middleware. Context holds ResponseWriter and *Request in it, with couple of other things.

Net Context has much more use cases then just for web services and goal was to keep that power. However, for web service development, having context in which handler is executed can potentially make bunch of stuff easier, since context is shared between middlewares and final handler. For other use cases that NetContext can be used for, Mezvaro context fully implements Net Context interface.

So, mezvaro is not compatible with standard library handlers?

It is. All handlers that respect http.Handler interface can be used. Also, all middlewares that rely on this interface (they have signature func(next http.Handler) http.Handler) like gorilla handlers can be used.

However, these handlers will not be able to use features that context provides, since they are not aware its existance. In order to use features that context provides, new handlers and middlewares have to be written.

Inspiration and credits

Much of inspiration for this library was taken from Gin framework. I think that Gin is great. However, it is full blown framework, which is not intention of this library. Also, Negroni middleware management library had influence on designing Mezvaro.

Router

Mezvaro does not bind itself to any router. It has been designed like that from the start and it is hardly going to change. Core library uses only dependencies from standard library (with net/context as addition). However, it is possible to use mezvaro with any router that respects http.Handler interface. In following days/weeks I will publish spearate projects for couple of most popular router libraries that will provide tighter integration with Mezvaro. For now, I am working on support for Gorilla Mux and HttpRouter support.

Example

More documentation is under way. However, for first glimpse, here are few short examples.

Hello world

package hello_world

import (
	"net/http"

	mv "github.com/delicb/mezvaro"
)

func main() {
	m := mv.New()
	m.UseFunc(func(c *mv.Context) {
		c.Response.Write([]byte("Hello world."))
	})
	http.Handle("/", m)
	http.ListenAndServe(":8000", nil)
}

Middleware

package middleware

import (
	"net/http"

	mv "github.com/delicb/mezvaro"
)

func HeaderMiddleware(c *mv.Context) {
	c.Response.Header().Set("Server", "Golang-Mezvaro")
	c.Next()
}

func HelloWorldHandler(c *mv.Context) {
	c.Response.Write([]byte("Response"))
}

func main() {
	m := mv.New()
	m.UseFunc(HeaderMiddleware, HelloWorldHandler)
	http.Handle("/", m)
	http.ListenAndServe(":8000", nil)
}

http.Handler middleware and handler

This example is identical to previous one, but it uses http.Handler interface and middleware to do same thing.

import (
	"net/http"

	mv "github.com/delicb/mezvaro"
)

func HeaderMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Server", "Golang-Mezvaro")
		next.ServeHTTP(w, r)
	})
}

func HelloWorldHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Response"))
}

func main() {
	m := mv.New()
	m.UseHandlerMiddleware(HeaderMiddleware)
	m.UseHandlerFunc(HelloWorldHandler)
	http.Handle("/", m)
	http.ListenAndServe(":8000", nil)
}

Context usage

This is simple example of usage of context to pass information between middlewares and handler. As mentioned earlier, Context implements net/context and can be used for setting timeout, deadline, getting cancel function or passing values (as shown in this example).

import (
	"fmt"
	"net/http"

	mv "github.com/delicb/mezvaro"
	"github.com/mssola/user_agent"
)

type BrowserInfo struct {
	Name    string
	Version string
	OS      string
}

type userInfo int

const browserInfoKey userInfo = 1

func UserAgentMiddleware(c *mv.Context) {
	ua := user_agent.New(c.Request.Header["User-Agent"][0])
	name, version := ua.Browser()
	browserInfo := BrowserInfo{
		Name:    name,
		Version: version,
		OS:      ua.OS(),
	}
	c.WithValue(browserInfoKey, browserInfo)
	c.Next()
}

func HelloWorldHandler(c *mv.Context) {
	browserInfo := c.Value(browserInfoKey).(BrowserInfo)
	msg := fmt.Sprintf(
		"You are accessing with browser: %s in version %s from OS: %s",
		browserInfo.Name, browserInfo.Version, browserInfo.OS,
	)
	c.Response.Write([]byte(msg))
}

func main() {
	m := mv.New()
	m.UseFunc(UserAgentMiddleware, HelloWorldHandler)
	http.Handle("/", m)
	http.ListenAndServe(":8000", nil)
}

Plans

This is something that I am playing with when I have free time. There is no guarantee that I will ever come back to this. If any futher development occurres, it will probably be breaking changes. However, feel free to report tickets and send pull requests if you find this aproach interesting. Any feedback is welcome.

Installation

To install Mezvaro, run go get github.com/delicb/mezvaro from command line.

About

GoLang middleware management library.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages