説明なし
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
Wijnand Modderman-Lenstra 2298f60ac9 More sugar 2年前
_examples Update example routes.md 2年前
docgen Ported middlewares 2年前
header Rewitten to maze.io/phi 2年前
middleware More sugar 2年前
.drone.yml Be verbose 2年前
.gitignore Ensure a route context is available 3年前
.travis.yml Travis: Build examples 2年前
CHANGELOG.md Update README, etc 2年前
CONTRIBUTING.md Add Travis widget to README 3年前
LICENSE Rewitten to maze.io/phi 2年前
README.md Ported middlewares 2年前
chain.go Ported middlewares 2年前
content.go Versioned package; Cleanups 2年前
context.go Added NotFound 2年前
mux.go Retrieve context from request context 2年前
mux_test.go Versioned package; Cleanups 2年前
param.go Versioned package; Cleanups 2年前
phi.go Ported middlewares 2年前
register.go Rewitten to maze.io/phi 2年前
render.go Convert Context to interface 2年前
tree.go Versioned package; Cleanups 2年前
tree_test.go Convert Context to interface 2年前

README.md

phi

GoDoc Widget Drone Widget Codebeat Widget

phi is a lightweight, idiomatic and composable router for building Go 1.7+ HTTP services. It’s especially good at helping you write large REST API services that are kept maintainable as your project grows and changes. phi is built on the new context package introduced in Go 1.7 to handle signaling, cancelation and request-scoped values across a handler chain.

The focus of the project has been to seek out an elegant and comfortable design for writing REST API servers, written during the development of the Pressly API service that powers our public API service and all of our client-side applications.

The key considerations of chi’s design are: project structure, maintainability, standard http handlers (stdlib-only), developer productivity, and deconstructing a large system into many small parts. The core router maze.io/phi is quite small (less than 1000 LOC), but we’ve also included some useful/optional subpackages: middleware, render and docgen. We hope you enjoy it too!

Features

  • Lightweight - cloc’d in <1000 LOC for the phi router
  • Fast - yes, see benchmarks
  • Designed for modular/composable APIs - middlewares, inline middlewares, route groups and subrouter mounting
  • Context control - built on new context package, providing value chaining, cancelations and timeouts
  • Robust - tested / used in production at Pressly.com, and many others
  • Doc generation - docgen auto-generates routing documentation from your source to JSON or Markdown
  • No external dependencies - plain ol’ Go 1.7+ stdlib + net/http

Examples

Examples:

  • rest - REST APIs made easy, productive and maintainable
  • limits - Timeouts and Throttling
  • todos-resource - Struct routers/handlers, an example of another code layout style
  • versions - Demo of chi/render subpkg
  • fileserver - Easily serve static files

Preview:

Here is a little preview of how routing looks like with phi. Also take a look at the generated routing docs in JSON (routes.json) and in Markdown (routes.md).

import (
  //...
  "context"
  phi "maze.io/phi.v1"
  "maze.io/phi.v1/middleware"
)

func main() {
  r := phi.NewRouter()

  // A good base middleware stack
  r.Use(middleware.RequestID)
  r.Use(middleware.RealIP)
  r.Use(middleware.Logger)
  r.Use(middleware.Recoverer)

  // When a client closes their connection midway through a request, the
  // http.CloseNotifier will cancel the request context (ctx).
  r.Use(middleware.CloseNotify)

  // Set a timeout value on the request context (ctx), that will signal
  // through ctx.Done() that the request has timed out and further
  // processing should be stopped.
  r.Use(middleware.Timeout(60 * time.Second))

  r.Get("/", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("hi"))
  })

  // RESTy routes for "articles" resource
  r.Route("/articles", func(r phi.Router) {
    r.With(paginate).Get("/", listArticles)  // GET /articles
    r.Post("/", createArticle)               // POST /articles
    r.Get("/search", searchArticles)         // GET /articles/search

    r.Route("/:articleID", func(r chi.Router) {
      r.Use(ArticleCtx)
      r.Get("/", getArticle)                 // GET /articles/123
      r.Put("/", updateArticle)              // PUT /articles/123
      r.Delete("/", deleteArticle)           // DELETE /articles/123
    })
  })

  // Mount the admin sub-router
  r.Mount("/admin", adminRouter())

  http.ListenAndServe(":3333", r)
}

func ArticleCtx(next phi.Handler) phi.Handler {
  return http.HandlerFunc(func(ctx *phi.Context) {
    articleID := ctx.Param("articleID")
    article, err := dbGetArticle(articleID)
    if err != nil {
      ctx.NotFound()
      return
    }
    ctx.SetValue("article", article)
    next.Serve(ctx)
  })
}

func getArticle(ctx *phi.Context) {
  article, ok := ctx.Value("article").(*Article)
  if !ok {
    ctx.Error(nil, 422)
    return
  }
  ctx.Write([]byte(fmt.Sprintf("title:%s", article.Title)))
}

// A completely separate router for administrator routes
func adminRouter() phi.Handler {
  r := phi.NewRouter()
  r.Use(AdminOnly)
  r.Get("/", adminIndex)
  r.Get("/accounts", adminListAccounts)
  return r
}

func AdminOnly(next phi.Handler) phi.Handler {
  return http.HandlerFunc(func(ctx *phi.Context) {
    perm, ok := ctx.Value("acl.permission").(YourPermissionType)
    if !ok || !perm.IsAdmin() {
      ctx.Error(nil, 403)
      return
    }
    next.Serve(ctx)
  })
}

Router design

Phi’s router is based on Chi’s router.

Chi’s router is based on a kind of Patricia Radix trie. Built on top of the tree is the Router interface:

// Router consisting of the core routing methods used by phi's Mux.
type Router interface {
	http.Handler
	Routes

	// Use appends one of more middlewares onto the Router stack.
	Use(middlewares ...func(Handler) Handler)

	// With adds inline middlewares for an endpoint handler.
	With(middlewares ...func(Handler) Handler) Router

	// Group adds a new inline-Router along the current routing
	// path, with a fresh middleware stack for the inline-Router.
	Group(fn func(r Router)) Router

	// Route mounts a sub-Router along a `pattern`` string.
	Route(pattern string, fn func(r Router)) Router

	// Mount attaches another Handler along ./pattern/*
	Mount(pattern string, h Handler)

	// Handle and HandleFunc adds routes for `pattern` that matches
	// all HTTP methods.
	Handle(pattern string, h Handler)
	HandleFunc(pattern string, h HandlerFunc)
  HTTPHandle(pattern string, h http.Handler)
  HTTPHandleFunc(pattern string, h http.HandlerFunc)

	// HTTP-method routing along `pattern`
	Connect(pattern string, h HandlerFunc)
	Delete(pattern string, h HandlerFunc)
	Get(pattern string, h HandlerFunc)
	Head(pattern string, h HandlerFunc)
	Options(pattern string, h HandlerFunc)
	Patch(pattern string, h HandlerFunc)
	Post(pattern string, h HandlerFunc)
	Put(pattern string, h HandlerFunc)
	Trace(pattern string, h HandlerFunc)

	// NotFound defines a handler to respond whenever a route could
	// not be found.
	NotFound(h HandlerFunc)
}

// Routes interface adds two methods for router traversal, which is also
// used by the `docgen` subpackage to generation documentation for Routers.
type Routes interface {
	// Routes returns the routing tree in an easily traversable structure.
	Routes() []Route

	// Middlewares returns the list of middlewares in use by the router.
	Middlewares() Middlewares
}

Each routing method accepts a URL pattern and chain of handlers. The URL pattern supports named params (ie. /users/:userID) and wildcards (ie. /admin/*).

Middleware handlers

// HTTP middleware setting a value on the request context
func Middleware(next Handler) Handler {
  return http.HandlerFunc(func(ctx *phi.Context) {
    ctx.SetValue("user", "123")
    next.Serve(ctx)
  })
}

Request handlers

// HTTP handler accessing data from the request context.
func Handler(ctx *phi.Context) {
  user := ctx.Value("user").(string)
  ctx.Write([]byte(fmt.Sprintf("hi %s", user)))
}
// HTTP handler accessing the url routing parameters.
func CtxHandler(ctx *phi.Context) {
  userID := ctx.Param("userID") // from a route like /users/:userID

  key := ctx.Value("key").(string)

  ctx.Write([]byte(fmt.Sprintf("hi %v, %v", userID, key)))
}

Middlewares

Chi comes equipped with an optional middleware package, providing:


Middleware Description
RequestID Injects a request ID into the context of each request.
RealIP Sets a http.Request’s RemoteAddr to either X-Forwarded-For or X-Real-IP.
Logger Logs the start and end of each request with the elapsed processing time.
Recoverer Gracefully absorb panics and prints the stack trace.
NoCache Sets response headers to prevent clients from caching.
CloseNotify Signals to the request context when a client has closed their connection.
Timeout Signals to the request context when the timeout deadline is reached.
Throttle Puts a ceiling on the number of concurrent requests.
Compress Gzip compression for clients that accept compressed responses.
Profiler Easily attach net/http/pprof to your routers.
Slashes Strip and redirect slashes on routing paths.
WithValue Short-hand middleware to set a key/value on the request context.

context

Credits

We’ll be more than happy to see your contributions!

License

Copyright © 2016-present maze

Licensed under MIT License