apirouter

package module
v0.0.16 Latest Latest
Warning

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

Go to latest
Published: Aug 23, 2019 License: MIT Imports: 15 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.

License Report Codacy Badge Build Status standard-readme compliant Release GoDoc

Table of Contents

Installation

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

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

Updating dependencies in go-api-router:

$ cd ../go-api-router
$ dep ensure -update -v
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
  • 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.12.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

@MrZ1836

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 GetClientIPAddress

func GetClientIPAddress(req *http.Request) string

GetClientIPAddress gets the client ip address

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) (params url.Values, ok bool)

GetParams gets the params from the http request (parsed once on log request)

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

JSONEncodeModels 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 NewStack added in v0.0.6

func NewStack() *stack

NewStack create a new stack

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 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()

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

StandardHandlerToHandle 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
}

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 stack. 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 stack
		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