Nenhuma descrição
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
Wijnand Modderman-Lenstra 2298f60ac9 More sugar 2 anos atrás
_examples Update example routes.md 2 anos atrás
docgen Ported middlewares 2 anos atrás
header Rewitten to maze.io/phi 2 anos atrás
middleware More sugar 2 anos atrás
.drone.yml Be verbose 2 anos atrás
.gitignore Ensure a route context is available 3 anos atrás
.travis.yml Travis: Build examples 3 anos atrás
CHANGELOG.md Update README, etc 3 anos atrás
CONTRIBUTING.md Add Travis widget to README 3 anos atrás
LICENSE Rewitten to maze.io/phi 2 anos atrás
README.md Ported middlewares 2 anos atrás
chain.go Ported middlewares 2 anos atrás
content.go Versioned package; Cleanups 2 anos atrás
context.go Added NotFound 2 anos atrás
mux.go Retrieve context from request context 2 anos atrás
mux_test.go Versioned package; Cleanups 2 anos atrás
param.go Versioned package; Cleanups 2 anos atrás
phi.go Ported middlewares 2 anos atrás
register.go Rewitten to maze.io/phi 2 anos atrás
render.go Convert Context to interface 2 anos atrás
tree.go Versioned package; Cleanups 2 anos atrás
tree_test.go Convert Context to interface 2 anos atrás

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