apirouter

package module
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Sep 10, 2020 License: MIT Imports: 16 Imported by: 18

README

go-api-router

Lightweight API httprouter middleware: cors, logging, and standardized error handling.

Release Build Status Report codecov Go Sponsor Donate


Table of Contents


Installation

go-api-router requires a supported release of Go.

go get -u github.com/mrz1836/go-api-router

Documentation

View the generated documentation

GoDoc

Features
  • Uses the fastest router: Julien Schmidt's httprouter
  • Uses gofrs uuid package to guarantee unique request ids
  • Uses MrZ's go-logger for either local or remote logging via Log Entries (Rapid7)
  • Uses MrZ's go-parameters for parsing any type of incoming parameter with ease
  • Added basic middleware support from Rileyr's middleware
  • Added additional CORS functionality
  • Standardized error responses for API requests
  • Centralized logging on all requests (requesting user info & request time)
  • Custom response writer for Etag and cache support
  • GetClientIPAddress() safely detects IP addresses behind load balancers
  • GetParams() parses parameters only once
  • FilterMap() removes any confidential parameters from logs
  • ...and more!
Package Dependencies
Library Deployment

goreleaser for easy binary or library deployment to Github and can be installed via: brew install goreleaser.

The .goreleaser.yml file is used to configure goreleaser.

Use make release-snap to create a snapshot version of the release, and finally make release to ship to production.

Makefile Commands

View all makefile commands

make help

List of all current commands:

all                  Runs lint, test-short and vet
clean                Remove previous builds and any test cache data
clean-mods           Remove all the Go mod cache
coverage             Shows the test coverage
godocs               Sync the latest tag with GoDocs
help                 Show this help message
install              Install the application
install-go           Install the application (Using Native Go)
lint                 Run the Go lint application
release              Full production release (creates release in Github)
release              Runs common.release then runs godocs
release-snap         Test the full release (build binaries)
release-test         Full production test release (everything except deploy)
replace-version      Replaces the version in HTML/JS (pre-deploy)
run-examples         Runs all the examples
tag                  Generate a new tag and push (tag version=0.0.0)
tag-remove           Remove a tag if found (tag-remove version=0.0.0)
tag-update           Update an existing tag to current commit (tag-update version=0.0.0)
test                 Runs vet, lint and ALL tests
test-short           Runs vet, lint and tests (excludes integration tests)
test-travis          Runs tests via Travis (also exports coverage)
uninstall            Uninstall the application (and remove files)
vet                  Run the Go vet application

Examples & Tests

All unit tests and examples run via Travis CI and uses Go version 1.15.x. View the deployment configuration file.

Run all tests (including integration tests)

make test

Run tests (excluding integration tests)

make test-short

Run the examples:

make run-examples

Benchmarks

Run the Go benchmarks:

make bench

Code Standards

Read more about this Go project's code standards.


Usage

View the examples

Basic implementation:

package main

import (
	"fmt"
	"net/http"

	"github.com/julienschmidt/httprouter"
	"github.com/mrz1836/go-api-router"
	"github.com/mrz1836/go-logger"
)

func main() {
	// Load the router & middleware
	router := apirouter.New()

	// Set the main index page (navigating to slash)
	router.HTTPRouter.GET("/", router.Request(index))

	// Serve the router!
	logger.Fatalln(http.ListenAndServe(":3000", router.HTTPRouter))
}

func index(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
	_, _ = fmt.Fprint(w, "This is a simple route example!")
}

Maintainers

MrZ
MrZ

Contributing

View the contributing guidelines and please follow the code of conduct.

How can I help?

All kinds of contributions are welcome 🙌! The most basic way to show your support is to star 🌟 the project, or to raise issues 💬. You can also support this project by becoming a sponsor on GitHub 👏 or by making a bitcoin donation to ensure this journey continues indefinitely! 🚀


License

License

Documentation

Overview

Package apirouter is a lightweight API router middleware for CORS, logging, and standardized error handling.

This package is intended to be used with Julien Schmidt's httprouter and uses MrZ's go-logger package.

Index

Examples

Constants

View Source
const (
	// ErrCodeUnknown unknown error code (example)
	ErrCodeUnknown int = 600

	// StatusCodeUnknown unknown HTTP status code (example)
	StatusCodeUnknown int = 600
)

Variables

This section is empty.

Functions

func FilterMap added in v0.1.12

func FilterMap(params *parameters.Params, filterOutFields []string) (filtered *parameters.Params)

FilterMap will filter the parameters and not log parameters with sensitive data. To add more parameters - see the if in the loop.

func FindString added in v0.0.10

func FindString(needle string, haystack []string) int

FindString returns the index of the first instance of needle in the array or -1 if it could not be found

func GetAuthToken added in v0.0.23

func GetAuthToken(req *http.Request) (token string, ok bool)

GetAuthToken gets the stored authentication token from the request

func GetClientIPAddress

func GetClientIPAddress(req *http.Request) string

GetClientIPAddress gets the client ip address

func GetCustomData added in v0.0.24

func GetCustomData(req *http.Request) (data interface{}, ok bool)

GetCustomData gets the stored custom data

func GetIPFromRequest added in v0.0.15

func GetIPFromRequest(req *http.Request) (ip string, ok bool)

GetIPFromRequest gets the stored ip from the request if found

func GetParams

func GetParams(req *http.Request) *parameters.Params

GetParams gets the params from the http request (parsed once on log request) Helper method for the parameters package

func GetRequestID added in v0.0.15

func GetRequestID(req *http.Request) (id string, ok bool)

GetRequestID gets the stored request id from the request

func JSONEncode added in v0.0.10

func JSONEncode(e *json.Encoder, objects interface{}, allowed []string) error

JSONEncode will encode only the allowed fields of the models

func JSONEncodeHierarchy added in v0.0.10

func JSONEncodeHierarchy(w io.Writer, objects interface{}, allowed interface{}) error

JSONEncodeHierarchy will execute JSONEncode for multiple nested objects

func NoCache added in v0.0.22

func NoCache(w http.ResponseWriter, req *http.Request)

NoCache is a simple piece of middleware that sets a number of HTTP headers to prevent a router (or subrouter) from being cached by an upstream proxy and/or client.

As per http://wiki.nginx.org/HttpProxyModule - NoCache sets:

Expires: Thu, 01 Jan 1970 00:00:00 UTC
Cache-Control: no-cache, private, max-age=0
X-Accel-Expires: 0
Pragma: no-cache (for HTTP/1.0 proxies/clients)

func PermitParams added in v0.0.17

func PermitParams(params *parameters.Params, allowedKeys []string)

PermitParams will remove all keys that not allowed Helper method for the parameters package

func ReturnJSONEncode added in v0.0.12

func ReturnJSONEncode(w http.ResponseWriter, code int, e *json.Encoder, objects interface{}, allowed []string) (err error)

ReturnJSONEncode is a mixture of ReturnResponse and JSONEncode

func ReturnResponse added in v0.0.3

func ReturnResponse(w http.ResponseWriter, req *http.Request, code int, data interface{})

ReturnResponse helps return a status code and message to the end user

func SetAuthToken added in v0.0.23

func SetAuthToken(req *http.Request, authToken string) *http.Request

SetAuthToken set the authentication token on the request

func SetCustomData added in v0.0.24

func SetCustomData(req *http.Request, data interface{}) *http.Request

SetCustomData set the custom data / user profile / permissions / etc

func SetOnRequest added in v0.0.23

func SetOnRequest(req *http.Request, keyName paramRequestKey, value interface{}) *http.Request

SetOnRequest will set the value on the request with the given key

func SnakeCase added in v0.0.10

func SnakeCase(str string) string

SnakeCase takes a camelCaseWord and breaks it into a camel_case_word

func StandardHandlerToHandle added in v0.0.7

func StandardHandlerToHandle(next http.Handler) httprouter.Handle

StandardHandlerToHandle converts a standard middleware to Julien handle version

Types

type APIError

type APIError struct {
	Code            int         `json:"code" url:"code"`                 // Associated error code
	Data            interface{} `json:"data" url:"data"`                 // Arbitrary data that is relevant
	InternalMessage string      `json:"-" url:"-"`                       // An internal message for engineers
	IPAddress       string      `json:"ip_address" url:"ip_address"`     // Current IP of user
	Method          string      `json:"method" url:"method"`             // Method requested (IE: POST)
	PublicMessage   string      `json:"message" url:"message"`           // Public error message
	RequestGUID     string      `json:"request_guid" url:"request_guid"` // Unique Request ID for tracking
	StatusCode      int         `json:"status_code" url:"status_code"`   // Associated HTTP status code (should be in request as well)
	URL             string      `json:"url" url:"url"`                   // Requesting URL
}

APIError is the enriched error message for API related errors

func ErrorFromRequest added in v0.0.16

func ErrorFromRequest(req *http.Request, internalMessage, publicMessage string, errorCode, statusCode int, data interface{}) *APIError

ErrorFromRequest gives an error without a response writer using the request

func ErrorFromResponse added in v0.0.16

func ErrorFromResponse(w *APIResponseWriter, internalMessage, publicMessage string, errorCode, statusCode int, data interface{}) *APIError

ErrorFromResponse generates a new error struct using CustomResponseWriter from LogRequest()

Example

ExampleErrorFromResponse example using ErrorFromResponse()

w := setupTest()
err := ErrorFromResponse(w, "internal message", "public message", ErrCodeUnknown, StatusCodeUnknown, `{"something":"else"}`)
fmt.Println(err.Error())
Output:

public message

func (*APIError) Error

func (e *APIError) Error() string

Error returns the string error message (only public message)

Example

ExampleAPIError_Error example using Error()

w := setupTest()
err := ErrorFromResponse(w, "internal message", "public message", ErrCodeUnknown, StatusCodeUnknown, `{"something":"else"}`)
fmt.Println(err.Error())
Output:

public message

func (*APIError) ErrorCode

func (e *APIError) ErrorCode() int

ErrorCode returns the error code

Example

ExampleAPIError_ErrorCode example using ErrorCode()

w := setupTest()
err := ErrorFromResponse(w, "internal message", "public message", ErrCodeUnknown, StatusCodeUnknown, `{"something":"else"}`)
fmt.Println(err.ErrorCode())
Output:

600

func (*APIError) Internal

func (e *APIError) Internal() string

Internal returns the string error message (only internal message)

Example

ExampleAPIError_Internal example using Internal()

w := setupTest()
err := ErrorFromResponse(w, "internal message", "public message", ErrCodeUnknown, StatusCodeUnknown, `{"something":"else"}`)
fmt.Println(err.Internal())
Output:

internal message

func (*APIError) JSON

func (e *APIError) JSON() (string, error)

JSON returns the entire public version of the error message

Example

ExampleAPIError_JSON example using JSON()

w := setupTest()
err := ErrorFromResponse(w, "internal message", "public message", ErrCodeUnknown, StatusCodeUnknown, `{"something":"else"}`)
str, _ := err.JSON()
fmt.Println(str)
Output:

{"code":600,"data":"{\"something\":\"else\"}","ip_address":"127.0.0.1","method":"GET","message":"public message","request_guid":"unique-guid-per-user","status_code":600,"url":"/this/path"}

type APIResponseWriter

type APIResponseWriter struct {
	http.ResponseWriter
	Buffer          bytes.Buffer  `json:"-" url:"-"`
	CacheIdentifier []string      `json:"cache_identifier" url:"cache_identifier"`
	CacheTTL        time.Duration `json:"cache_ttl" url:"cache_ttl"`
	IPAddress       string        `json:"ip_address" url:"ip_address"`
	Method          string        `json:"method" url:"method"`
	NoWrite         bool          `json:"no_write" url:"no_write"`
	RequestID       string        `json:"request_id" url:"request_id"`
	Status          int           `json:"status" url:"status"`
	URL             string        `json:"url" url:"url"`
	UserAgent       string        `json:"user_agent" url:"user_agent"`
}

APIResponseWriter wraps the ResponseWriter and stores the status of the request. It is used by the LogRequest middleware

func (*APIResponseWriter) AddCacheIdentifier

func (r *APIResponseWriter) AddCacheIdentifier(identifier string)

AddCacheIdentifier add cache identifier to the response writer

func (*APIResponseWriter) Header

func (r *APIResponseWriter) Header() http.Header

Header returns the http.Header that will be written to the response

func (*APIResponseWriter) StatusCode added in v0.0.5

func (r *APIResponseWriter) StatusCode() int

StatusCode give a way to get the status code

func (*APIResponseWriter) Write

func (r *APIResponseWriter) Write(data []byte) (int, error)

Write writes the data out to the client, if WriteHeader was not called, it will write status http.StatusOK (200)

func (*APIResponseWriter) WriteHeader

func (r *APIResponseWriter) WriteHeader(status int)

WriteHeader will write the header to the client, setting the status code

type AllowedKeys added in v0.0.10

type AllowedKeys map[string]interface{}

AllowedKeys is for allowed keys

type InternalStack added in v0.0.18

type InternalStack struct {
	// contains filtered or unexported fields
}

InternalStack internal stack type

func NewStack added in v0.0.6

func NewStack() *InternalStack

NewStack will create an InternalStack struct

func (*InternalStack) Use added in v0.0.18

func (s *InternalStack) Use(mw Middleware)

Use adds the middle ware to the list

func (*InternalStack) Wrap added in v0.0.18

Wrap wraps the router

type Middleware added in v0.0.6

type Middleware func(httprouter.Handle) httprouter.Handle

Middleware is the Handle implementation

func StandardHandlerToMiddleware added in v0.0.8

func StandardHandlerToMiddleware(next http.Handler) Middleware

StandardHandlerToMiddleware converts a standard middleware to type Middleware

type Router added in v0.0.2

type Router struct {
	AccessControlExposeHeaders  string             `json:"access_control_expose_headers" url:"access_control_expose_headers"`   // Allow specific headers for cors
	CrossOriginAllowCredentials bool               `json:"cross_origin_allow_credentials" url:"cross_origin_allow_credentials"` // Allow credentials for BasicAuth()
	CrossOriginAllowHeaders     string             `json:"cross_origin_allow_headers" url:"cross_origin_allow_headers"`         // Allowed headers
	CrossOriginAllowMethods     string             `json:"cross_origin_allow_methods" url:"cross_origin_allow_methods"`         // Allowed methods
	CrossOriginAllowOrigin      string             `json:"cross_origin_allow_origin" url:"cross_origin_allow_origin"`           // Custom value for allow origin
	CrossOriginAllowOriginAll   bool               `json:"cross_origin_allow_origin_all" url:"cross_origin_allow_origin_all"`   // Allow all origins
	CrossOriginEnabled          bool               `json:"cross_origin_enabled" url:"cross_origin_enabled"`                     // Enable or Disable CrossOrigin
	FilterFields                []string           `json:"filter_fields" url:"filter_fields"`                                   // Filter out protected fields from logging
	HTTPRouter                  *httprouter.Router `json:"-" url:"-"`                                                           // J Schmidt's httprouter
	SkipLoggingPaths            []string           `json:"skip_logging_paths" url:"skip_logging_paths"`                         // Skip logging on these paths (IE: /health)
}

Router is the configuration for the middleware service

func New

func New() *Router

New returns a router middleware configuration to use for all future requests

func (*Router) BasicAuth added in v0.0.2

func (r *Router) BasicAuth(h httprouter.Handle, requiredUser, requiredPassword string, errorResponse interface{}) httprouter.Handle

BasicAuth wraps a request for Basic Authentication (RFC 2617)

func (*Router) Request added in v0.0.2

func (r *Router) Request(h httprouter.Handle) httprouter.Handle

Request will write the request to the logs before and after calling the handler

func (*Router) RequestNoLogging added in v0.0.2

func (r *Router) RequestNoLogging(h httprouter.Handle) httprouter.Handle

RequestNoLogging will just call the handler without any logging Used for API calls that do not require any logging overhead

func (*Router) SetCrossOriginHeaders added in v0.0.4

func (r *Router) SetCrossOriginHeaders(w http.ResponseWriter, req *http.Request, _ httprouter.Params)

SetCrossOriginHeaders sets the cross-origin headers if enabled todo: combine this method and the GlobalOPTIONS http.HandlerFunc() method

type Stack added in v0.0.6

type Stack interface {
	/*
		Adds a middleware to the InternalStack. MWs will be
		called in the same order that they are added,
		such that:
			Use(Request ID Middleware)
			Use(Request Timing Middleware)
		would result in the request id middleware being
		the outermost layer, called first, before the
		timing middleware.
	*/
	// Use for adding new middlewares
	Use(Middleware)

	/*
		Wraps a given handle with the current InternalStack
		from the result of Use() calls.
	*/
	// Wrap wraps the router
	Wrap(httprouter.Handle) httprouter.Handle
}

Stack is the interface for middleware

Directories

Path Synopsis
Package main shows examples using the API Router
Package main shows examples using the API Router

Jump to

Keyboard shortcuts

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