apirouter

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Dec 3, 2019 License: MIT Imports: 16 Imported by: 18

README

go-api-router

go-api-router is a lightweight API httprouter middleware: cors, logging, and standardized error handling. Extends Julien Schmidt's httprouter package.

Build Status Report Codacy Badge Release standard-readme compliant GoDoc

Table of Contents

Installation

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

$ go get -u github.com/mrz1836/go-api-router
Package Dependencies

Documentation

You can view the generated documentation here.

Features
  • Uses the fastest router: Julien Schmidt's httprouter
  • Uses Satori's go.uuid package to guarantee unique request ids
  • Uses MrZ's go-logger for either local or remote logging via LogEntries
  • 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

Examples & Tests

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

Run all tests (including integration tests)

$ cd ../go-api-router
$ go test ./... -v

Run tests (excluding integration tests)

$ cd ../go-api-router
$ go test ./... -v -test.short

View and run the examples:

$ cd ../go-api-router/examples
$ go run examples.go

Benchmarks

Run the Go benchmarks:

$ cd ../go-api-router
$ go test -bench . -benchmem

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 API example!")
}

Maintainers

@MrZ

Contributing

This project uses Julien Schmidt's httprouter package.

This project uses Satori's go.uuid package.

This project uses Rileyr's middleware package.

View the contributing guidelines and follow the code of conduct.

Support the development of this project 🙏

Donate

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
)

Variables

This section is empty.

Functions

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 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:"-"`                       // 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
	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 string, publicMessage string, errorCode 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 string, publicMessage string, errorCode 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, `{"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, `{"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, `{"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, `{"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, `{"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","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 create a new InternalStack

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 {
	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
	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

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