Help us shape the future of Go! Take the 2026 Go Developer Survey

gemux

package module
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 29, 2019 License: MIT Imports: 4 Imported by: 0

README

Good Enough Multiplexer (gemux)

Godoc CircleCI

gemux is the good enough multiplexer. It aims to provide functionality that is good enough for the majority of HTTP services.

Usage

package main

func main() {
    mux := new(gemux.ServeMux)

    mux.Handle("/", http.MethodGet, http.HandlerFunc(healthHandler))
    mux.Handle("/posts", http.MethodGet, http.HandlerFunc(getPostsHandler))
    mux.Handle("/posts", http.MethodPost, http.HandlerFunc(createPostHandler))
    mux.Handle("/posts/*", http.MethodDelete, http.HandlerFunc(deletePostHandler))
    mux.Handle("/posts/*/comments", http.MethodPost, http.HandlerFunc(createCommentHandler))
    mux.Handle("/posts/*/comments", http.MethodGet, http.HandlerFunc(getCommentsHandler))
    mux.Handle("/posts/*/comments/*", http.MethodDelete, http.HandlerFunc(deleteCommentHandler))

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

Rationale

In every single HTTP API I write:

  • I want to multiplex requests based on the path and the method of the request
  • I want to be able to capture path parameters (e.g. capture 42 from /posts/42)

So this is all the functionality gemux provides. This is good enough for almost all HTTP APIs. If it isn't good enough for yours, you should use another router, such as gorilla/mux.

Features

Strict Path Based Routing (with wildcards)

Route strictly based on paths, but allow wildcards for path parameters such as a resource ID.

mux.Handle("/users", http.MethodPost, http.HandlerFunc(getUsersHandler))
mux.Handle("/posts", http.MethodPost, http.HandlerFunc(createPostHandler))
mux.Handle("/posts/*", http.MethodGet, http.HandlerFunc(getPostHandler))
Strict Method Based Routing (with wildcards)

Route based on methods, and allow wildcard methods if you need to write your own method multiplexer (or don't care about methods for some reason).

mux.Handle("/users", http.MethodGet, http.HandlerFunc(createPostHandler)) // implement your own method muxer
mux.Handle("/posts", "*", http.HandlerFunc(createPostHandler)) // implement your own method muxer
Context Path Parameters

Extract path wildcard values via the request context.

func getPostHandler(w http.ResponseWriter, r *http.Request) {
    rawPostID := gemux.PathParameter(r.Context(), 0)
    postID, err := strconv.ParseInt(rawPostID, 10, 64)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    // ...
}
Custom Error Handlers

Create custom error handlers for when a route or method isn't found.

mux := new(gemux.ServeMux)
mux.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusNotFound)
    json.NewEncoder(w).Encode(map[string]string{"error": "not found"})
})
mux.MethodNotAllowedHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusMethodNotAllowed)
    json.NewEncoder(w).Encode(map[string]string{"error": "method not allowed"})
})
No External Dependencies

gemux does not depend upon any libraries outside of the standard library.

It's Fast... Enough.

gemux isn't slow, and it's not fast either. If you're writing an HTTP service you're probably doing a bunch of I/O, so your multiplexer speed is almost totally insignificant. Since gemux doesn't try to do anything clever to be fast (the root of all evil) it's more maintainable than other multiplexers.

Won't Fix

There many features common in HTTP multiplexers that gemux does not and will not support.

Subrouters

Subrouters are often a source of complexity without any real advantages. If you need subrouters, you can implement your own solution using this library, or just use another library like gorilla/mux.

HandleFunc

There's no reason to have two methods that do the same thing. If you have an http.HandlerFunc, wrap it with http.HandlerFunc to make it an http.Handler.

Middlewares

A middleware should just be a function that takes an http.Handler and returns one. Since you can already use middlewares this way, there's no reason for gemux to handle them.

Method Helpers

There's no reason to have multiple methods for doing the same thing. If you need to handle a GET, just pass http.MethodGet to gemux.ServeMux.Handle.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func MethodNotAllowedHandler

func MethodNotAllowedHandler() http.Handler

MethodNotAllowedHandler returns a simple request handler that replies to each request with a "405 method not allowed" reply and writes the 405 status code.

func PathParameter added in v0.2.0

func PathParameter(ctx context.Context, n int) string

PathParameter returns the nth path parameter from the request context. It returns an empty string if no value exists at the given index.

Example
mux := new(ServeMux)

handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	fmt.Println(PathParameter(ctx, 0))
	fmt.Println(PathParameter(ctx, 1))
})

mux.Handle("/foo/*/bar/*", http.MethodGet, handler)

rw := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo/test/bar/92", nil)
mux.ServeHTTP(rw, req)
fmt.Println(rw.Body.String())
Output:
test
92

Types

type ServeMux

type ServeMux struct {

	// NotFoundHandler is called when there is no path corrosponding to
	// the request URL. If NotFoundHandler is nil, http.NotFoundHandler
	// will be used.
	NotFoundHandler http.Handler

	// MethodNotAllowedHandler is called when there is no method corrosponding
	// to the request URL. If MethodNotAllowedHandler is nil, MethodNotAllowedHandler
	// will be used.
	MethodNotAllowedHandler http.Handler
	// contains filtered or unexported fields
}

ServeMux is an HTTP request multiplexer. It matches the URL and method of the incoming request against a list of registered routes, and calls the matching route.

Example
mux := new(ServeMux)

mux.Handle("/", http.MethodGet, stringHandler("health check"))
mux.Handle("/posts", http.MethodGet, stringHandler("create post"))
mux.Handle("/posts", http.MethodGet, stringHandler("get posts"))
mux.Handle("/posts/*", http.MethodGet, stringHandler("get post"))
mux.Handle("/posts/*/comments", http.MethodGet, stringHandler("get post comments"))

rw := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
mux.ServeHTTP(rw, req)
fmt.Println(rw.Body.String())

rw = httptest.NewRecorder()
req, _ = http.NewRequest("GET", "/posts/4", nil)
mux.ServeHTTP(rw, req)
fmt.Println(rw.Body.String())

rw = httptest.NewRecorder()
req, _ = http.NewRequest("GET", "/posts/5/comments", nil)
mux.ServeHTTP(rw, req)
fmt.Println(rw.Body.String())
Output:
health check
get post
get post comments

func (*ServeMux) Handle

func (mux *ServeMux) Handle(pattern string, method string, handler http.Handler)

Handle registers a handler for the given pattern and method on the muxer. The pattern should be the exact URL to match, with the exception of wildcards ("*"), which can be used for a single segment of a path (split on "/") to match anything. A wildcard method of "*" can also be used to match any method.

func (*ServeMux) ServeHTTP

func (mux *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP dispatches the request to the handler whose pattern and method matches the request URL and method.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL