endpoint

package module
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Aug 9, 2017 License: MIT Imports: 7 Imported by: 0

README

endpoint - dependency injection and wrapping for http handlers

GoDoc

Install:

go get github.com/BlueOwlOpenSource/endpoint

This package attempts to solve several issues with http endpoint handlers:

  • Chaining actions to create composite handlers
  • Wrapping endpoint handlers with before/after actions
  • Dependency injection for endpoint handlers
  • Binding handlers to their endpoints next to the code that defines the endpoints
  • Delaying initialization code execution until services are started allowing services that are not used to remain uninitialized
  • Reducing the cost of refactoring endpoint handlers passing data directly from producers to consumers without needing intermediaries to care about what is being passed
  • Avoid the overhead of verifying that requested items are present when passed indirectly (a common problem when using context.Context to pass data indirectly)

It does this by defining endpoints as a sequence of handler functions. Handler functions come in different flavors. Data is passed from one handler to the next using reflection to match the output types with input types requested later in the handler chain. To the extent possible, the handling is precomputed so there is very little reflection happening when the endpoint is actually invoked.

Endpoints are registered to services before or after the service is started.

When services are pre-registered and started later, it is possible to bind endpoints to them in init() functions and thus colocate the endpoint binding with the endpoint definition and spread the endpoint definition across multiple files and/or packages.

Services come in two flavors: one flavor binds endpoints with http.HandlerFunc and the other binds using mux.Router.

Example uses include:

  • Turning output structs into json
  • Common error handling
  • Common logging
  • Common initialization
  • Injection of resources and dependencies

Small Example

CreateEndpoint is the simplest way to start using the endpoint framework. It generates an http.HandlerFunc from a list of handlers. The handlers will be called in order. In the example below, first WriteErrorResponse() will be called. It has an inner() func that it uses to invoke the rest of the chain. When WriteErrorResponse() calls its inner() function, the db injector returned by InjectDB is called. If that does not return error, then the inline function below to handle the endpint is called.

mux := http.NewServeMux()
mux.HandleFunc("/my/endpoint", endpoint.CreateEndpoint(
	WriteErrorResponse,
	InjectDB("postgres", "postgres://..."),
	func(r *http.Request, db *sql.DB, w http.ResponseWriter) error {
		// Write response to w or return error...
		return nil
	}))

WriteErrorResponse invokes the remainder of the handler chain by calling inner().

func WriteErrorResponse(inner func() endpoint.TerminalError, w http.ResponseWriter) {
	err := inner()
	if err != nil {
		w.Write([]byte(err.Error()))
		w.WriteHeader(500)
	}
}

InjectDB returns a handler function that opens a database connection. If the open fails, executation of the handler chain is terminated.

func InjectDB(driver, uri string) func() (endpoint.TerminalError, *sql.DB) {
	return func() (endpoint.TerminalError, *sql.DB) {
		db, err := sql.Open(driver, uri)
		if err != nil {
			return err, nil
		}
		return nil, db
	}
}

Documentation

Overview

Package endpoint is a general purpose lightweight non-opinionated web server framework that provides a concise way to handle errors and inject dependencies into http endpoint handlers.

Why

Composite endpoints: endpoints are assembled from a collection of handlers.

Before/after actions: the middleware handler type wraps the rest of the handler chain so that it can both inject items that are used downstream and process return values.

Dependency injection: injectors, static injectors, and fallible injectors can all be used to provide data and code to downstream handlers. Downstream handlers request what they need by including appropriate types in their argument lists. Injectors are invoked only if their outputs are consumed.

Code juxtaposition: when using pre-registered services, endpoint binding can be registered next to the code that implements the endpoint even if the endpoints are implemented in multiple files and/or packages.

Delayed initialization: initializers for pre-registered services are only executed when the service is started and bound to an http server. This allow code to define such endpoints to depend on resources that may not be present unless the service is started.

Reduced refactoring cost: handlers and endpoints declare their inputs and outputs in the argument lists and return lists. Handlers only need to know about their own inputs and outputs. The endpoint framework carries the data to where it is needed. It does so with a minimum of copies and without recursive searches (see context.Context). Type checking is done at service start time (or endpoint binding time when binding to services that are already running).

Lower overhead indirect passing: when using context.Context to pass values indirectly, the Go type system cannot be used to verify types at compile time or startup time. Endpoint verifies types at startup time allowing code that receives indirectly-passed data simpler. As much as possible, work is done at initialization time rather than endpoint invocation time.

Basics

To use the endpoint package, create services first. After that the endpoints can be registered to the service and the service can be started.

A simpler way to use endpoint is to use the CreateEndpoint function. It converts a list of handlers into an http.HandlerFunc. This bypasses service creation and endpoint registration. See https://github.com/BlueOwlOpenSource/endpoint/blob/master/README.md for an example.

Terminology

Service is a collection of endpoints that can be started together and may share a handler collection.

Handler is a function that is used to help define an endpoint.

Handler collection is a group of handlers.

Downstream handlers are handlers that are to the right of the current handler in the list of handlers. They will be invoked after the current handler.

Upstream handlers are handlers that are to the left of the current handler in the list of handlers. They will have already been invoked by the time the current handler is invoked.

Services

A service allows a group of related endpoints to be started together. Each service may have a set of common handlers that are shared among all the endpoints registered with that service.

Services come in four flavors: started or pre-registered; with Mux or with without.

Pre-registered services are not initialized until they are Start()ed. This allows them to depend upon resources that may not may not be available without causing a startup panic unless they're started without their required resources. It also allows endpoints to be registered in init() functions next to the definition of the endpoint.

Handlers

A list of handlers will be invoked from left-to-right. The first handler in the list is invoked first and the last one (the endpoint) is invoked last. The handlers do not directly call each other -- rather the framework manages the invocations. Data provided by one handler can be used by any handler to its right and then as the handlers return, the data returned can be used by any handler to its left. The data provided and required is identified by its type. Since Go makes it easy to make aliases of types, it is easy to make types distinct. When there is not an exact match of types, the framework will use the closest (in distance) value that can convert to the required type.

Each handler function is distinguished by its position in the handler list and by its primary signature: its arguments and return values. In Go, types may be named or unnamed. Unnamed function types are part of primary signature. Named function types are not part of the primary signature.

These are the types that are recognized as valid handlers: Static Injectors, Injectors, Endpoints, and Middleware.

Injectors are only invoked if their output is consumed or they have no output. Middleware handlers are (currently) always invoked.

Injectors

There are three kinds of injectors: static injectors, injectors, and fallible injectors.

Injectors and static injectors have the following type signature:

func(input value(s)) output values(s)

None of the input or output parameters may be un-named functions. That describes nearly every function in Go. Handlers that match a more specific type signature are that type, rather than being an injector or static injector.

Injectors whose output values are not used by a downstream handler are dropped from the handler chain. They are not invoked. Injectors that have no output values are a special case and they are always retained in the handler chain.

Static injectors are called exactly once per endpoint. They are called when the endpoint is started or when the endpoint is registered -- whichever comes last.

Values returned by static injectors will be shared by all invocations of the endpoint.

Injectors are called once per endpoint invocation (or more if they are downstream from a middleware handler that calls inner() more than once).

Injectors a distingued from static injectors by either their position in the handler list or by the parameters that they take. If they take http.ResponseWriter or *http.Request, then they're not static. Anything that is downstream of a non-static injector or middleware handler is also not static.

Fallible injectors are injectors whose first return values is of type endpoint.TerminalError:

func(input value(s)) (TerminalError, output values(s))

If a non-nil value is returned as the TerminalError from a fallible injector, none of the downstream handlers will be called. The handler chain returns from that point with the TerminalError as a return value. Since all return values must be consumed by a middleware handler, fallible injectors must come downstream from a middleware handler that takes TerminalError as a returned value. If a fallible injector returns nil for the TerminalError, the other output values are made available for downstream handlers to consume. The other output values are not considered return values and are not available to be consumed by upstream middleware handlers.

Some examples:

func staticInjector(i int, s string) int { return i+7 }

func injector(r *http.Request) string { return r.FormValue("x") }

func fallibleInjector(i int) endpoint.TerminalError {
	if i > 10 {
		return fmt.Errorf("limit exceeded")
	}
	return nil
}

Middleware handlers

Middleware handlers wrap the handlers downstream in a inner() function that they may call. The type signature of a middleware handler is a function that receives an function as its first parameter. That function must be of an anonymous type:

// middleware handler
func(innerfunc, input value(s)) return value(s)

// innerfunc
func(output value(s)) returned value(s)

For example:

func middleware(inner func(string) int, i int) int {
	j := inner(fmt.Sprintf("%d", i)
	return j * 2
}

When this middleware function runs, it is responsible for invoking the rest of the handler chain. It does this by calling inner(). The parameters to inner are available as inputs to downstream handlers. The value(s) returned by inner come from the return values of downstream middleware handlers and the endpoint handler.

Middleware handlers can call inner() zero or more times.

The values returned by middleware handlers must be consumed by another upstream middlware handler.

Endpoint Handlers

Endpoint handlers are simply the last handler in the handler chain. They look like regular Go functions. Their input parameters come from other handlers. Their return values (if any) must be consumed by an upstream middleware handler.

func(input value(s)) return values(s)

Panics

Endpoint will panic during endpoint registration if the provided handlers do not constitute a valid chain. For example, if a some handler requires a FooType but there is no upstream handler that provides a FooType then the handler list is invalid and endpoint will panic.

Endpoint should not panic after initialization.

Example

The code below puts up a test http server, hits the /example endpoint, decodes the response, prints it, and exits. This is just to excercise the endpoint defined above. The interesting stuff happens above.

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"

	"github.com/BlueOwlOpenSource/endpoint"
	"github.com/gorilla/mux"
)

// The endpoint framework distinguishes parameters based on their types.
// All parameters of type "string" look the same, but a type that is
// defined as another type (like exampleType) is a different type.
type exampleType string
type fooParam string
type fromMiddleware string

// exampleStaticInjector will not be called until the service.Start()
// call in Example_PreRegisterServiceWithMux.  It will be called only
// once per endpoint registered.  Since it has a return value, it will
// only run if a downstream handler consumes the value it returns.
//
// The values returned by injectors and available as input parameters
// to any downstream handler.
func exampleStaticInjector() exampleType {
	return "example static value"
}

// exampleInjector will be called for each request.  We know that
// exampleInjector is a regular injector because it takes a parameter
// that is specific to the request (*http.Request).
func exampleInjector(r *http.Request) fooParam {
	return fooParam(r.FormValue("foo"))
}

type returnValue interface{}

// jsonifyResult wraps all handlers downstream of it in the call chain.
// We know that jsonifyResult is a middleware handler because its first
// argument is an function with an anonymous type (inner).   Calling inner
// invokes all handlers downstream from jsonifyResult.  The value returned
// by inner can come from the return values of the final endpoint handler
// or from values returned by any downstream middleware.  The parameters
// to inner are available as inputs to any downstream handler.
//
// Parameters are matched by their types.  Since inner returns a returnValue,
// it can come from any downstream middleware or endpoint that returns something
// of type returnValue.
func jsonifyResult(inner func(fromMiddleware) returnValue, w http.ResponseWriter) {
	v := inner("jsonify!")
	w.Header().Set("Content-Type", "application/json")
	encoded, _ := json.Marshal(v)
	w.WriteHeader(200)
	w.Write(encoded)
}

// Endpoints are grouped and started by services.  Handlers that are
// common to all endpoints are attached to the service.
var service = endpoint.PreRegisterServiceWithMux("example-service",
	exampleStaticInjector,
	jsonifyResult)

func init() {
	// The /example endpoint is bound to a handler chain
	// that combines the functions included at the service
	// level and the functions included here.  The final chain is:
	//	exampleStaticInjector, jsonifyResult, exampleInjector, exampleEndpoint.
	// ExampleStaticInjector and jsonifyResult come from the service
	// definition.  ExampleInjector and exampleEndpoint are attached when
	// the endpoint is registered.
	//
	// Handlers will execute in the order of the chain: exampleStaticInjector
	// then jsonifyResult.  When jsonifyResult calls inner(), exampleInjector
	// runs, then exampleEndpoint.   When exampleEndpoint returns, inner() returns
	// so jsonifyResult continues its work.  When jsonifyResult returns, the
	// handler chain is complete and the http server can form a reply from the
	// ResponseWriter.
	//
	// Since service is WithMux, we can use gorilla mux modifiers when
	// we register endpoints.  This allows us to trivially indicate that our
	// example endpoint supports the GET method only.
	service.RegisterEndpoint(
		"/example", exampleInjector, exampleEndpoint).Methods("GET")
}

// This is the final endpoint handler.  The parameters it takes can
// be provided by any handler upstream from it.  It can also take the two
// values that are included by the http handler signature: http.ResponseWriter
// and *http.Request.
//
// Any values that the final endpoint handler returns must be consumed by an
// upstream middleware handler.  In this example, a "returnValue" is returned
// here and consumed by jsonifyResult.
func exampleEndpoint(sv exampleType, foo fooParam, mid fromMiddleware) returnValue {
	return map[string]string{
		"value": fmt.Sprintf("%s-%s-%s", sv, foo, mid),
	}
}

// The code below puts up a test http server, hits the /example
// endpoint, decodes the response, prints it, and exits.  This
// is just to excercise the endpoint defined above.  The interesting
// stuff happens above.
func main() {
	muxRouter := mux.NewRouter()
	service.Start(muxRouter)
	localServer := httptest.NewServer(muxRouter)
	defer localServer.Close()
	r, err := http.Get(localServer.URL + "/example?foo=bar")
	if err != nil {
		fmt.Println("get error", err)
		return
	}
	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		fmt.Println("read error", err)
		return
	}
	var res map[string]string
	err = json.Unmarshal(buf, &res)
	if err != nil {
		fmt.Println("unmarshal error", err)
		return
	}
	fmt.Println("Value:", res["value"])
}
Output:

Value: example static value-bar-jsonify!

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AnnotateCallsInner

func AnnotateCallsInner(handlerOrCollection interface{}) interface{}

AnnotateCallsInner wraps middleware handlers and marks that the middleware handler as guaranteed to call inner(). This is important only in the case when a downstream handler is returning a value consumed by an upstream middleware handler and that value has no zero value that reflect can generate in the event that inner() is not called. Without AnnotateCallsInner(), some handler sequences may be rejected due to being unable to create zero values.

Using AnnotateCallsInner on other handlers is a no-op.

EXPERIMENTAL FEATURE. MAY BE REMOVED.

func AnnotateNotStatic

func AnnotateNotStatic(handlerOrCollection interface{}) interface{}

AnnotateNotStatic wraps any handler func argument and marks that handler as not being a static injector. Since handlers will be invoked in order, this also implies that nothing that follows this handler is a static injector.

For example:

var InjectCurrentTime = AnnotateNotStatic(func() time.Time {
	return time.Now()
})

EXPERIMENTAL FEATURE. MAY BE REMOVED.

func AnnotateSideEffects

func AnnotateSideEffects(handlerOrCollection interface{}) interface{}

AnnotateSideEffects wraps any handler func argument and marks that handler as having side effects. Normally an injector that produces at least one type and none of the types that the injector produce are consumed dropped from the dropped from the handler list. If AnnotateSideEffects() is used to wrap that injector, then the injector will be invoked even though its output is not used.

For example:

var counter = 0
var counterLock sync.Mutex
type InvocationCounter int
var CountEndpointInvocations = AnnotateSideEffects(func() InvocationCounter {
	counterLock.Lock()
	counter++
	counterLock.Unlock()
	return InvocationCounter(counter)
})

EXPERIMENTAL FEATURE. MAY BE REMOVED.

func AutoInject

func AutoInject(v interface{}) injectionWish

AutoInject makes a promise that values will be provided to the service via service.Inject() before the service is started. This is done by either providing an example of the type or by providing a reflect.Type. The value provided is not retained, just the type of the value provided.

This is only relevant for pre-registered services.

EXPERIMENTAL FEATURE. MAY BE REMOVED.

func CreateEndpoint

func CreateEndpoint(funcs ...interface{}) func(http.ResponseWriter, *http.Request)

CreateEndpoint generates a http.HandlerFunc from a list of handlers. This bypasses Service, ServiceRegistration, ServiceWithMux, and ServiceRegistrationWithMux. The static initializers are invoked immedately.

Example

CreateEndpoint is the simplest way to start using the endpoint framework. It generates an http.HandlerFunc from a list of handlers. The handlers will be called in order. In the example below, first WriteErrorResponse() will be called. It has an inner() func that it uses to invoke the rest of the chain. When WriteErrorResponse() calls its inner() function, the db injector returned by InjectDB is called. If that does not return error, then the inline function below to handle the endpint is called.

package main

import (
	"database/sql"
	"net/http"

	"github.com/BlueOwlOpenSource/endpoint"
)

// CreateEndpoint is the simplest way to start using the endpoint framework.  It
// generates an http.HandlerFunc from a list of handlers.  The handlers will be called
// in order.   In the example below, first WriteErrorResponse() will be called.  It
// has an inner() func that it uses to invoke the rest of the chain.  When
// WriteErrorResponse() calls its inner() function, the db injector returned by
// InjectDB is called.  If that does not return error, then the inline function below
// to handle the endpint is called.
func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/my/endpoint", endpoint.CreateEndpoint(
		WriteErrorResponse,
		InjectDB("postgres", "postgres://..."),
		func(r *http.Request, db *sql.DB, w http.ResponseWriter) error {
			// Write response to w or return error...
			return nil
		}))
}

// WriteErrorResponse invokes the remainder of the handler chain by calling inner().
func WriteErrorResponse(inner func() endpoint.TerminalError, w http.ResponseWriter) {
	err := inner()
	if err != nil {
		w.Write([]byte(err.Error()))
		w.WriteHeader(500)
	}
}

// InjectDB returns a handler function that opens a database connection.   If the open
// fails, executation of the handler chain is terminated.
func InjectDB(driver, uri string) func() (endpoint.TerminalError, *sql.DB) {
	return func() (endpoint.TerminalError, *sql.DB) {
		db, err := sql.Open(driver, uri)
		if err != nil {
			return err, nil
		}
		return nil, db
	}
}
Output:

func GetTypeCode

func GetTypeCode(a interface{}) typeCode

GetTypeCode maps Go types to integers. It is exported for testing purposes.

Types

type EndpointBinder

type EndpointBinder func(path string, fn http.HandlerFunc)

EndpointBinder is the signature of the binding function used to start a ServiceRegistration.

type EndpointRegistration

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

type EndpointRegistrationWithMux

type EndpointRegistrationWithMux struct {
	EndpointRegistration
	// contains filtered or unexported fields
}

EndpointRegistrationWithMux holds endpoint definitions for services that will be Start()ed with gorilla mux. Most of the gorilla mux methods can be used with these endpoint definitions.

func (*EndpointRegistrationWithMux) BuildOnly

BuildOnly applies the mux.Route method of the same name to this endpoint when the endpoint is initialized.

func (*EndpointRegistrationWithMux) BuildVarsFunc

Applies the mux.Route method of the same name to this endpoint when the endpoint is initialized.

func (*EndpointRegistrationWithMux) GetError

func (r *EndpointRegistrationWithMux) GetError() error

Calls the mux.Route method of the same name on the route created for this endpoint.

func (*EndpointRegistrationWithMux) GetHandler

func (r *EndpointRegistrationWithMux) GetHandler() http.Handler

Calls the mux.Route method of the same name on the route created for this endpoint.

func (*EndpointRegistrationWithMux) GetHostTemplate

func (r *EndpointRegistrationWithMux) GetHostTemplate() (string, error)

Calls the mux.Route method of the same name on the route created for this endpoint.

func (*EndpointRegistrationWithMux) GetName

func (r *EndpointRegistrationWithMux) GetName() string

Calls the mux.Route method of the same name on the route created for this endpoint.

func (*EndpointRegistrationWithMux) GetPathTemplate

func (r *EndpointRegistrationWithMux) GetPathTemplate() (string, error)

Calls the mux.Route method of the same name on the route created for this endpoint.

func (*EndpointRegistrationWithMux) Headers

Applies the mux.Route method of the same name to this endpoint when the endpoint is initialized.

func (*EndpointRegistrationWithMux) HeadersRegexp

Applies the mux.Route method of the same name to this endpoint when the endpoint is initialized.

func (*EndpointRegistrationWithMux) Host

Applies the mux.Route method of the same name to this endpoint when the endpoint is initialized.

func (*EndpointRegistrationWithMux) Match

func (r *EndpointRegistrationWithMux) Match(req *http.Request, match *mux.RouteMatch) bool

Calls the mux.Route method of the same name on the route created for this endpoint.

func (*EndpointRegistrationWithMux) MatcherFunc

Applies the mux.Route method of the same name to this endpoint when the endpoint is initialized.

func (*EndpointRegistrationWithMux) Methods

Applies the mux.Route method of the same name to this endpoint when the endpoint is initialized.

func (*EndpointRegistrationWithMux) Name

Applies the mux.Route method of the same name to this endpoint when the endpoint is initialized.

func (*EndpointRegistrationWithMux) Path

Applies the mux.Route method of the same name to this endpoint when the endpoint is initialized.

func (*EndpointRegistrationWithMux) PathPrefix

Applies the mux.Route method of the same name to this endpoint when the endpoint is initialized.

func (*EndpointRegistrationWithMux) Queries

Applies the mux.Route method of the same name to this endpoint when the endpoint is initialized.

func (*EndpointRegistrationWithMux) Route

func (r *EndpointRegistrationWithMux) Route() (*mux.Route, error)

Route returns the *mux.Route that has been registered to this endpoint, if possible.

func (*EndpointRegistrationWithMux) Schemes

Applies the mux.Route method of the same name to this endpoint when the endpoint is initialized.

func (*EndpointRegistrationWithMux) SkipClean

func (r *EndpointRegistrationWithMux) SkipClean() bool

Calls the mux.Route method of the same name on the route created for this endpoint.

func (*EndpointRegistrationWithMux) URL

func (r *EndpointRegistrationWithMux) URL(pairs ...string) (*url.URL, error)

Calls the mux.Route method of the same name on the route created for this endpoint.

func (*EndpointRegistrationWithMux) URLHost

func (r *EndpointRegistrationWithMux) URLHost(pairs ...string) (*url.URL, error)

Calls the mux.Route method of the same name on the route created for this endpoint.

func (*EndpointRegistrationWithMux) URLPath

func (r *EndpointRegistrationWithMux) URLPath(pairs ...string) (*url.URL, error)

Calls the mux.Route method of the same name on the route created for this endpoint.

type HandlerCollection

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

HandlerCollection provides a way to bundle up several handlers so that they can be refered to together as a set. A HanlderCollection can be used as a handler func in any place that a handler func can be used including NewHandlerCollection(), PreRegisterService(), and PreRegisterEndpoint.

func NewHandlerCollection

func NewHandlerCollection(name string, funcs ...interface{}) *HandlerCollection

NewHandlerCollection creates a collection of handlers.

The name of the collection is used for error messages and is otherwise irrelevant.

The passed in funcs must match one of the handler types.

When combining handler collections and lists of handlers, static injectors may be pulled from handler collections and treated as static injectors even if the handler collection as a whole comes after a regular injector.

func NewHandlerCollectionWithEndpoint

func NewHandlerCollectionWithEndpoint(name string, funcs ...interface{}) *HandlerCollection

NewHandlerCollectionWithEndpoint creates a handler collection that includes an endpoint.

type Service

type Service struct {
	Name string
	// contains filtered or unexported fields
}

Service allows a group of related endpoints to be started together. This form of service represents an already-started service that binds its enpoints using a simple binder like http.ServeMux.HandleFunc().

func RegisterService

func RegisterService(name string, binder EndpointBinder, funcs ...interface{}) *Service

RegisterService creates a service and starts it immediately.

func (*Service) RegisterEndpoint

func (s *Service) RegisterEndpoint(path string, funcs ...interface{}) *EndpointRegistration

RegisterEndpoint registers and immedately starts an endpoint. The provided funcs must all match one of handler types. The functions provided are invoked in-order. Static injectors first and the endpoint last.

The return value does not need to be retained -- it is also remembered in the Service.

type ServiceRegistration

type ServiceRegistration struct {
	Name string
	// contains filtered or unexported fields
}

ServiceRegistration allows a group of related endpoints to be started together. This form of service represents pre-registered service service that binds its enpoints using a simple binder like http.ServeMux.HandleFunc(). None of the endpoints associated with this service will initialize themsleves or start listening until Start() is called.

func PreRegisterService

func PreRegisterService(name string, funcs ...interface{}) *ServiceRegistration

PreRegisterService creates a service that must be Start()ed later.

The passed in funcs follow the same rules as for the funcs in a HandlerCollection.

The injectors and middlware functions will preceed any injectors and middleware specified on each endpoint that registeres with this service.

PreRegsteredServices do not initialize or bind to handlers until they are Start()ed.

The name of the service is just used for error messages and is otherwise ignored.

func (*ServiceRegistration) Inject

func (s *ServiceRegistration) Inject(v interface{}) *ServiceRegistration

Inject provides a value for one of the types promised by AutoInject.

func (*ServiceRegistration) RegisterEndpoint

func (s *ServiceRegistration) RegisterEndpoint(path string, funcs ...interface{}) *EndpointRegistration

RegisterEndpoint pre-registers an endpoint. The provided funcs must all match one of the handler types. The functions provided are invoked in-order. Static injectors first and the endpoint last.

The return value does not need to be retained -- it is also remembered in the ServiceRegistration.

The endpoint initialization will not run until the service is started. If the service has already been started, the endpoint will be started immediately.

func (*ServiceRegistration) Start

func (s *ServiceRegistration) Start(binder EndpointBinder) *Service

Start runs all staticInjectors for all endpoints pre-registered with this service. Bind all endpoints and starts listening. Start() may only be called once.

type ServiceRegistrationWithMux

type ServiceRegistrationWithMux struct {
	Name string
	// contains filtered or unexported fields
}

ServiceRegistrationWithMux allows a group of related endpoints to be started together. This form of service represents pre-registered service service that binds its enpoints using gorilla mux.Router.HandleFunc. None of the endpoints associated with this service will initialize themsleves or start listening until Start() is called.

func PreRegisterServiceWithMux

func PreRegisterServiceWithMux(name string, funcs ...interface{}) *ServiceRegistrationWithMux

PreRegisterServiceWithMux creates a service that must be Start()ed later.

The passed in funcs follow the same rules as for the funcs in a HandlerCollection.

The injectors and middlware functions will preceed any injectors and middleware specified on each endpoint that registeres with this service.

PreRegsteredServices do not initialize or bind to handlers until they are Start()ed.

The name of the service is just used for error messages and is otherwise ignored.

func (*ServiceRegistrationWithMux) Inject

func (s *ServiceRegistrationWithMux) Inject(v interface{}) *ServiceRegistrationWithMux

Inject provides a value for one of the types promised by AutoInject.

func (*ServiceRegistrationWithMux) RegisterEndpoint

func (s *ServiceRegistrationWithMux) RegisterEndpoint(path string, funcs ...interface{}) *EndpointRegistrationWithMux

RegisterEndpoint pre-registers an endpoint. The provided funcs must all match one of the handler types. The functions provided are invoked in-order. Static injectors first and the endpoint last.

The return value does not need to be retained -- it is also remembered in the Service. The return value can be used to add mux.Route-like modifiers. They will not take effect until the service is started.

The endpoint initialization will not run until the service is started. If the service has already been started, the endpoint will be started immediately.

func (*ServiceRegistrationWithMux) Start

Start calls endpoints initializers for this Service and then registers all the endpoint handlers to the router. Start() should be called at most once.

type ServiceWithMux

type ServiceWithMux struct {
	Name string
	// contains filtered or unexported fields
}

ServiceWithMux allows a group of related endpoints to be started together. This form of service represents an already-started service that binds its enpoints using gorilla mux.Router.HandleFunc.

func RegisterServiceWithMux

func RegisterServiceWithMux(name string, router *mux.Router, funcs ...interface{}) *ServiceWithMux

RegisterServiceWithMux creates a service and starts it immediately.

func (*ServiceWithMux) RegisterEndpoint

func (s *ServiceWithMux) RegisterEndpoint(path string, funcs ...interface{}) *mux.Route

RegisterEndpoint registers and immediately starts an endpoint. The provided funcs must all match one of the handler types. The functions provided are invoked in-order. Static injectors first and the endpoint last.

type TerminalError

type TerminalError interface {
	error
}

TerminalError is a standard error interface. For fallible injectors (matching the handlerFallibleInjectorType type signature), TerminalError must be the first return value.

A non-nil return value terminates the handler call chain. The TerminalError return value (like other return values) must be consumed by an upstream handler.

Jump to

Keyboard shortcuts

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