https://github.com/morikuni/yacm
yacm provides a way to build a http handlers with middlewares
https://github.com/morikuni/yacm
go http middleware
Last synced: 3 months ago
JSON representation
yacm provides a way to build a http handlers with middlewares
- Host: GitHub
- URL: https://github.com/morikuni/yacm
- Owner: morikuni
- License: mit
- Created: 2016-02-21T14:19:22.000Z (about 10 years ago)
- Default Branch: master
- Last Pushed: 2017-05-09T14:41:25.000Z (almost 9 years ago)
- Last Synced: 2024-06-20T11:48:58.400Z (almost 2 years ago)
- Topics: go, http, middleware
- Language: Go
- Homepage:
- Size: 60.5 KB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# yacm(八雲) - yet another composable middleware
[](https://travis-ci.org/morikuni/yacm)
[](https://godoc.org/github.com/morikuni/yacm)
yacm provides a way to build a http handlers with middlewares.
## Install
```sh
go get github.com/morikuni/yacm
```
## Design
A typical middleware stack is like this.
```go
type Middleware func(http.Handler) http.Handler
// from net/http
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
```
Composition rule is
```
Middleware + Handler = Handler
```
and some libraries provide
```
Middleware + Middleware = Middleware
```
```
+----------------+
| Client |
+---+--------^---+
| |
req| res|
| |
+---v--------+---+ ----------+
| Middleware | |
+---+--------^---+ |
| | |
req| res| |
| | |
+---v--------+---+ -+ |
| Middleware | | |Handler
+---+--------^---+ | |
| | | |
req| res| |Handler |
| | | |
+---v--------+---+ | |
| Handler | | |
+----------------+ -+ -------+
```
yacm's stack is this.
```go
type Filter interface {
WrapService(w http.ResponseWriter, r *http.Request, s Service) error
}
type Service interface {
TryServeHTTP(w http.ResponseWriter, r *http.Request) error
}
type Catcher interface {
CatchError(w http.ResponseWriter, r *http.Request, err error) error
}
type Shutter interface {
ShutError(w http.ResponseWriter, r *http.Request, err error)
}
// from net/http
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
```
`Service` corresponds to `Handler`, and `Filter` corresponds to `Middleware`.
The difference is that these may return an error.
Composition rule is
```
Filter + Filter = Filter
Filter + Service = Service
Cather + Cather = Cather
Cather + Shutter = Shutter
Service + Shutter = Handler
```
```
+----------------+
| Client |
+---+--------^---+
| |
req| res+----------+
| | |
+------- +- +---v--------+---+ | +---------------+ ----------+
| | | Filter +--+ +--+ Shutter | |
| | +---+--------^---+ | | +------------^--+ |
| | | | | | | |
| Filter| req| res| | |res err| |
| | | | | | | |
| | +---v--------+---+ | | +------------+--+ -+ |
Service| | | Filter +--+ +--+ Catcher | | |Shutter
| +- +---+--------^---+ | | +------------^--+ | |
| | | | | | | |
| req| res| | +------+ err| |Catcher |
| | | | | | | |
| +---v--------+---+ | +---+--------+--+ | |
| | Service +--+------> Catcher | | |
+---------- +----------------+ err +---------------+ -+ -------+
```
You can also use `Handler` as a `Service`, `Middleware` as a `Filter`.
## Example
```go
package main
import (
"errors"
"log"
"net/http"
"github.com/morikuni/yacm"
)
func Logging(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println(r.Method, r.URL)
h.ServeHTTP(w, r)
})
}
var ErrNotGet = errors.New("method is not GET")
func GetOnly(w http.ResponseWriter, r *http.Request, next yacm.Service) error {
if r.Method != "GET" {
return ErrNotGet
}
return next.TryServeHTTP(w, r)
}
func Catcher(w http.ResponseWriter, r *http.Request, err error) error {
switch err {
case ErrNotGet:
w.WriteHeader(http.StatusMethodNotAllowed)
w.Write([]byte(http.StatusText(http.StatusMethodNotAllowed)))
return nil
default:
return err
}
}
func main() {
env := yacm.NewEnv().
AppendMiddlewares(Logging).
AppendFilterFuncs(GetOnly).
AppendCatcherFuncs(Catcher)
http.Handle("/hello", env.ServeFunc(func(w http.ResponseWriter, r *http.Request) error {
w.Write([]byte("hello"))
return nil
}))
http.Handle("/error", env.ServeFunc(func(w http.ResponseWriter, r *http.Request) error {
// Since this error will never be handled by Catcher,
// it will be 500 internal server error by yacm.DefaultShutter
return errors.New("unknown error")
}))
http.ListenAndServe(":8080", nil)
}
```