ws

package
v2.2.2 Latest Latest
Warning

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

Go to latest
Published: Sep 11, 2020 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package ws defines types used by framework and application components involved in web service processing. For more information on how web services work in Granitic, see https://granitic.io/ref/web-services

A brief explanation of the key types and concepts follows.

Requests and responses

Request and Response are abstractions of the HTTP request and response associated with a call to a web service endpoint. By default your application logic will not have access to the underlying HTTP objects (this can be overridden on a per-endpoint basis by setting AllowDirectHTTPAccess to true on your handler - see the package documentation for ws/handler for more information).

Your application code will not directly control how data is parsed into a Request or how the data and/or errors in a Response are rendered to the caller. This is instead handled by the JSONWs or XMLWs facility.

HTTP status codes are determined automatically based on the type (or lack of) errors in the Response object, but this behaviour can be overridden by setting an HTTP status code manually on the Response.

Errors

Errors can be detected or occur during all the phases of request processing (see https://granitic.io/ref/web-services-principles ). If errors are encountered during the parsing and binding phases of request processing, they are referred to as 'framework errors' as they are handled outside of application code. Framework errors result in one of small number of generic error messages being sent to a caller. See https://granitic.io/ref/error-handling for information on how to override these messages or how to allow your application to have visibility of framework errors.

If an error occurs during or after parsing and binding is complete, it will be recorded in the WsReponse.Errors field. These types of errors are called service errors. For more information on service errors, refer to the GoDoc for CategorisedError below or https://granitic.io/ref/service-error-management

Response writing

The serialisation of the data in a Response to an HTTP response is handled by a component implementing ResponseWriter. A component of this type will be automatically created for you when you enable the JSONWs or XMLWs facility.

Parameter binding

Parameter binding refers to the process of automatically capturing request query parameters and injecting them into fields on the Request Body. It also refers to a similar process for extracting information from a request's path using regular expressions. See https://granitic.io/ref/capture-web-service-data for more details.

IAM and versioning

Granitic does not provide implementations of Identity Access Management or request versioning, but instead provides highly generic types to allow your application's implementations of these concepts to be integrated with Grantic's web service request processing. See the GoDoc for Identifier, AccessChecker and handler/WsVersionAssessor and the iam package for more details.

HTTP status code determination

Unless your application defines its own HTTPStatusCodeDeterminer, the eventual HTTP status code set on the response to a web service request it determined by examining the state of a Response using the following logic:

1. If the Response.HTTPStatus field is non-zero, use that.

2. If the Response.Errors.HTTPStatus field is non-zero, use that.

3. If the Response.Errors structure:

a) Contains one or more 'Unexpected' errors, use HTTP 500.

b) Contains an 'HTTP' error, convert that error's code to a number and use that.

c) Contains one or more 'Security' errors, use HTTP 401.

d) Contains one or more 'Client' errors, use HTTP 400.

e) Contains one or more 'Logic' errors, use HTTP 409.

4. Return HTTP 200.

Index

Constants

View Source
const (
	// Unexpected is an unhandled error that will generally result in an HTTP 500 status code being set.
	Unexpected = iota

	// Client is a problem that the calling client has caused or could have foreseen, generally resulting in an HTTP 400 status code.
	Client

	// Logic is a problem that the calling client could not be expected to have foreseen (email address in use, for example) resulting in an HTTP 409.
	Logic

	// Security is an access or authentication error that might result in an HTTP 401 or 403.
	Security

	// HTTP is an error that forces a specific HTTP status code.
	HTTP
)
View Source
const (
	// Unmarshall indicates an error was encountered while trying to parse an HTTP request body into a struct
	Unmarshall = iota

	// QueryBind indicates an error was encountered while mapping HTTP query parameters to fields on a struct
	QueryBind

	//PathBind indicates an error was encountered while mapping elements of an HTTP request's path to fields on a struct
	PathBind
)
View Source
const (
	// UnableToParseRequest indicates that the HTTP request could not be parsed
	UnableToParseRequest = "UnableToParseRequest"
	// QueryTargetNotArray indicates that a query parameter whose value is a list has been bound to a target field that is not an array
	QueryTargetNotArray = "QueryTargetNotArray"

	// QueryWrongType indicates that a query parameter is not compatible with the type of field to which it is bound
	QueryWrongType = "QueryWrongType"

	// PathWrongType indicates that a path element is not compatible with the type of field to which it is bound
	PathWrongType = "PathWrongType"

	// QueryNoTargetField indicates that no field on the target can be matched to the a named query parameter
	QueryNoTargetField = "QueryNoTargetField"
)
View Source
const (
	// Normal is a normal outcome resulting in an HTTP 200 response.
	Normal = iota

	// Error is an outcome with anticipated and handled errors resulting in a 4xx response.
	Error

	// Abnormal is an unexpected or unusual outcome resulting in a 5xx response.
	Abnormal
)

Variables

This section is empty.

Functions

func CategoryToCode

func CategoryToCode(c ServiceErrorCategory) string

CategoryToCode maps a ServiceErrorCategory to the category's name's first letter. For example, Security maps to 'S'

func CategoryToName

func CategoryToName(c ServiceErrorCategory) string

CategoryToName maps a ServiceErrorCategory to the category's name's first letter. For example, Security maps to 'Security'

func MergeHeaders

func MergeHeaders(res *Response, ch map[string]string, dh map[string]string) map[string]string

MergeHeaders merges together the headers that have been defined on the Response, the static default headers attache to this writer and (optionally) those constructed by the ws.CommonResponseHeaderBuilder attached to this writer. The order of precedence, from lowest to highest, is static headers, constructed headers, headers in the Response.

func NewParamsForPath

func NewParamsForPath(targets []string, values []string) *types.Params

NewParamsForPath creates a Params used to store the elements of a request path extracted using regular expression groups.

func NewParamsForQuery

func NewParamsForQuery(values url.Values) *types.Params

NewParamsForQuery creates a Params storing the HTTP query parameters from a request.

func RecoverIDFunction added in v2.1.0

func RecoverIDFunction(ctx context.Context) func(context.Context) string

RecoverIDFunction finds a function in the context that is able to recover a request ID from a context.

func RequestID added in v2.1.0

func RequestID(ctx context.Context) string

RequestID returns the ID stored in the supplied context or "" if there is no ID or if a function for extracting an ID from a context has not been defined and also stored in the context

func StoreRequestIDFunction added in v2.1.0

func StoreRequestIDFunction(ctx context.Context, f func(context.Context) string) context.Context

StoreRequestIDFunction stores a function in the context that is able to recover a request ID from a context.

func WriteHeaders

func WriteHeaders(w http.ResponseWriter, headers map[string]string)

WriteHeaders writes the supplied map as HTTP headers.

Types

type AbnormalStatusWriter

type AbnormalStatusWriter interface {
	// Write converts whatever data is present in the supplied state object to the HTTP output stream associated
	// with the current web service request.
	WriteAbnormalStatus(ctx context.Context, state *ProcessState) error
}

AbnormalStatusWriter is implemented by components able to write a valid response even if the request resulted in an abnormal (5xx) outcome.

type AccessChecker

type AccessChecker interface {
	// Allowed returns true if the caller is allowed to have this request processed, false otherwise.
	Allowed(ctx context.Context, r *Request) bool
}

AccessChecker is implemented by components that are able to determine if a caller is allowed to have a request processed.

type CategorisedError

type CategorisedError struct {
	// The broad type of error, which influences the eventual HTTP status code set on the response.
	Category ServiceErrorCategory

	// A unique code that a caller can rely on to identify a specific error or that can be used to lookup an error message.
	Code string

	// A message suitable for displaying to the caller.
	Message string

	//If this error relates to a specific field or parameter in a web service request, this field is set to the name of that field.
	Field string
}

CategorisedError is a service error with a concept of the general 'type' of error it is.

func NewCategorisedError

func NewCategorisedError(category ServiceErrorCategory, code string, message string) *CategorisedError

NewCategorisedError creates a new CategorisedError with every field expect 'Field' set.

type CommonResponseHeaderBuilder

type CommonResponseHeaderBuilder interface {
	BuildHeaders(ctx context.Context, state *ProcessState) map[string]string
}

CommonResponseHeaderBuilder is an object that constructs response headers that are common to all web service requests. These may typically be caching instructions or 'processing server' records. Implementations must be extremely cautious when using the information in the supplied WsProcess state as some values may be nil.

type DirectHTTPAccess

type DirectHTTPAccess struct {
	// The HTTP response output stream.
	ResponseWriter http.ResponseWriter

	// The incoming HTTP request.
	Request *http.Request
}

DirectHTTPAccess wraps the underlying low-level HTTP request and response writing objects.

type ErrorFormatter

type ErrorFormatter interface {
	// FormatErrors converts the supplied errors into a structure that a response writer will use to write the errors to
	// the current HTTP response.
	FormatErrors(errors *ServiceErrors) interface{}
}

ErrorFormatter is an interface for components able to convert a set of service errors into a structure suitable for serialisation.

type FrameworkError

type FrameworkError struct {
	// The phase of the request processing during which an error was encountered.
	Phase FrameworkPhase

	// The name of the field or parameter in the HTTP request with a problem
	ClientField string

	// The name of the field on the response body struct that was being written to
	TargetField string

	// A system generated message relating to the error.
	Message string

	// The value of the field/parameter that caused the error.
	Value string

	// For array parameters, the position in the array that caused the error.
	Position int

	// A system generated code for the error.
	Code string
}

FrameworkError an error encountered in early phases of request processing, before application code is invoked.

func NewPathBindFrameworkError

func NewPathBindFrameworkError(message, code, target string) *FrameworkError

NewPathBindFrameworkError creates a FrameworkError with fields set appropriate for an error encountered during mapping elements of the HTTP request's path to fields on a Request's Body.

func NewQueryBindFrameworkError

func NewQueryBindFrameworkError(message, code, param, target string) *FrameworkError

NewQueryBindFrameworkError creates a FrameworkError with fields set appropriate for an error encountered during mapping of HTTP query parameters to fields on a Request's Body

func NewUnmarshallFrameworkError

func NewUnmarshallFrameworkError(message, code string) *FrameworkError

NewUnmarshallFrameworkError creates a FrameworkError with fields set appropriate for an error encountered during parsing of the HTTP request body.

func (*FrameworkError) Error

func (fe *FrameworkError) Error() string

Error returns a string representation of a FrameworkError

func (*FrameworkError) RecordField

func (fe *FrameworkError) RecordField(f string)

RecordField implements FieldAssociatedError

type FrameworkErrorEvent

type FrameworkErrorEvent string

FrameworkErrorEvent uniquely identifies a 'handled' failure during the parsing and binding phases

type FrameworkErrorGenerator

type FrameworkErrorGenerator struct {
	Messages        map[FrameworkErrorEvent][]string
	HTTPMessages    map[string]string
	FrameworkLogger logging.Logger
}

A FrameworkErrorGenerator can create error messages for errors that occur outside of application code and messages that should be displayed when generic HTTP status codes (404, 500, 503 etc) are set.

func (*FrameworkErrorGenerator) Error

Error creates a service error given a framework error.

func (*FrameworkErrorGenerator) HTTPError

func (feg *FrameworkErrorGenerator) HTTPError(status int, a ...interface{}) *CategorisedError

HTTPError generates a message to be displayed to a caller when a generic HTTP status (404 etc) is encountered. If an error message is not defined for the supplied status, the message "HTTP (code)" is returned, e.g. "HTTP 101"

func (*FrameworkErrorGenerator) MessageCode

func (feg *FrameworkErrorGenerator) MessageCode(e FrameworkErrorEvent, a ...interface{}) (message string, code string)

MessageCode returns a message and code for a Framework error event (leaving the caller to create a CategorisedError)

type FrameworkPhase

type FrameworkPhase int

FrameworkPhase is the phase of the request processing during which an error was encountered.

type GraniticHTTPStatusCodeDeterminer

type GraniticHTTPStatusCodeDeterminer struct {
	NoError    int
	Client     int
	Security   int
	Unexpected int
	Logic      int
}

GraniticHTTPStatusCodeDeterminer is the default HTTPStatusCodeDeterminer used by Granitic's XXXWs facilities. The actual HTTP status codes returned can be customised using the fields on this struct. Use NewGraniticHTTPStatusCodeDeterminer() to achieve the default behaviour described in this package's package documentation

func NewGraniticHTTPStatusCodeDeterminer added in v2.1.0

func NewGraniticHTTPStatusCodeDeterminer() *GraniticHTTPStatusCodeDeterminer

NewGraniticHTTPStatusCodeDeterminer creates a GraniticHTTPStatusCodeDeterminer using the HTTP response codes described in this package's package documentation

func (*GraniticHTTPStatusCodeDeterminer) DetermineCode

func (dhscd *GraniticHTTPStatusCodeDeterminer) DetermineCode(response *Response) int

DetermineCode examines the response and returns an HTTP status code according to the rules defined at the top of this GoDoc page.

type HTTPStatusCodeDeterminer

type HTTPStatusCodeDeterminer interface {
	// DetermineCode returns the HTTP status code that should be set on the response.
	DetermineCode(response *Response) int
}

HTTPStatusCodeDeterminer implemented by a component able to choose the most appropriate HTTP status code to set given the state of a Response

type Identifier

type Identifier interface {
	// Identify returns information about the caller derived request and a Context that might be different from the supplied Context.
	Identify(ctx context.Context, req *http.Request) (iam.ClientIdentity, context.Context)
}

Identifier is implemented by components that are able to identify a caller based on a raw HTTP request (normally from headers and cookies). Implementations of this interface may return a new Context that supersedes the supplied Context.

type MarshalingWriter

type MarshalingWriter interface {

	// MarshalAndWrite converts the data to some serialisable form (JSON, XML etc) and writes it to the HTTP output stream.
	MarshalAndWrite(data interface{}, w http.ResponseWriter) error
}

MarshalingWriter is implemented by components that can convert the supplied data into a form suitable for serialisation and write that serialised form to the HTTP output stream.

type MarshallingResponseWriter

type MarshallingResponseWriter struct {
	// Injected automatically
	FrameworkLogger logging.Logger

	// Component able to calculate the HTTP status code that should be written to the HTTP response.
	StatusDeterminer HTTPStatusCodeDeterminer

	// Component able to generate errors if a problem is encountered during marshalling.
	FrameworkErrors *FrameworkErrorGenerator

	// The common and static set of headers that should be written to all responses.
	DefaultHeaders map[string]string

	// Component able to wrap response data in a standardised structure.
	ResponseWrapper ResponseWrapper

	// Component able to dynamically generate additional headers to be written to the response.
	HeaderBuilder CommonResponseHeaderBuilder

	// Component able to format services errors in an application specific manner.
	ErrorFormatter ErrorFormatter

	// Component able to serialize the data to the HTTP output stream.
	MarshalingWriter MarshalingWriter

	// Whether or not the unique ID assigned to the request should be written as a response header
	IncludeRequestID bool

	// The header key used if the request ID should be written as a response header
	RequestIDHeader string
}

MarshallingResponseWriter is a response writer that uses automatic marshalling of structs to serialisable forms rather than using templates.

func (*MarshallingResponseWriter) Write

func (rw *MarshallingResponseWriter) Write(ctx context.Context, state *ProcessState, outcome Outcome) error

Write implements ResponseWriter.Write

func (*MarshallingResponseWriter) WriteAbnormalStatus

func (rw *MarshallingResponseWriter) WriteAbnormalStatus(ctx context.Context, state *ProcessState) error

WriteAbnormalStatus implements AbnormalStatusWriter.WriteAbnormalStatus

type Outcome

type Outcome uint

Outcome is an enumeration of the high-level result of processing a request. Used internally.

type ParamBinder

type ParamBinder struct {
	// Injected by Granitic
	FrameworkLogger logging.Logger

	// Source of service errors for errors encountered while binding.
	FrameworkErrors *FrameworkErrorGenerator
}

ParamBinder takes string parameters extracted from an HTTP request, converts them to Go native or Granitic nilable types and injects them into the RequestBody on a Request.

func (*ParamBinder) AutoBindQueryParameters

func (pb *ParamBinder) AutoBindQueryParameters(wsReq *Request)

AutoBindQueryParameters takes the query parameters from an HTTP request and injects them into fields on the Request.RequestBody assuming the parameters have exactly the same name as the target fields. Any errors encountered are recorded as framework errors in the Request.

func (*ParamBinder) BindPathParameters

func (pb *ParamBinder) BindPathParameters(wsReq *Request, p *types.Params)

BindPathParameters takes strings extracted from an HTTP's request path (using regular expression groups) and injects them into fields on the Request.RequestBody. Any errors encountered are recorded as framework errors in the Request.

func (*ParamBinder) BindQueryParameters

func (pb *ParamBinder) BindQueryParameters(wsReq *Request, targets map[string]string)

BindQueryParameters takes the query parameters from an HTTP request and injects them into fields on the Request.RequestBody using the keys of the supplied map as the name of the target fields. Any errors encountered are recorded as framework errors in the Request.

type ProcessState

type ProcessState struct {
	// The representation of the incoming request at the time processing completed or failed.
	WsRequest *Request

	// The representation of the data to be sent to the caller at the time processing completed or failed.
	WsResponse *Response

	// The HTTP output stream.
	HTTPResponseWriter *httpendpoint.HTTPResponseWriter

	// Errors detected while processing the web service request. If set, supersedes the errors present in Response field.
	ServiceErrors *ServiceErrors

	// Information about the caller or user of the web service.
	Identity iam.ClientIdentity

	// The HTTP status code to be set on the HTTP response.
	Status int
}

ProcessState is wrapper for current state of request processing. This type is used by components implementing ResponseWriter. Because a request may fail at many points during processing, there is no guarantee that any of the fields in this type are set, valid or complete, so this type must be used with caution.

func NewAbnormalState

func NewAbnormalState(status int, w *httpendpoint.HTTPResponseWriter) *ProcessState

NewAbnormalState creates a new ProcessState for a request that has resulted in an abnormal (HTTP 5xx) outcome).

type Request

type Request struct {
	// The HTTP method (GET, POST etc) of the underlying HTTP request.
	HTTPMethod string

	// If the HTTP request had a body and if the handler that generated this Request implements WsUnmarshallTarget,
	// then RequestBody will contain a struct representation of the request body.
	RequestBody interface{}

	// A copy of the HTTP query parameters from the underlying HTTP request with type-safe accessors.
	QueryParams *types.Params

	// Information extracted from the path portion of the HTTP request using regular expression groups with type-safe accessors.
	PathParams []string

	// Problems encountered during the parsing and binding phases of request processing.
	FrameworkErrors []*FrameworkError

	// Information about the web service caller (if the handler has a Identifier).
	UserIdentity iam.ClientIdentity

	// The underlying HTTP request and response  (if the handler was configured to pass
	// this information on).
	UnderlyingHTTP *DirectHTTPAccess

	// The component name of the handler that generated this Request.
	ServingHandler string

	// The unique ID assigned to this request and stored in the context
	ID func(ctx context.Context) string
	// contains filtered or unexported fields
}

Request stores information about a web service request that has been either copied in or derived from an underlying HTTP request.

func (*Request) AddFrameworkError

func (wsr *Request) AddFrameworkError(f *FrameworkError)

AddFrameworkError records a framework error.

func (*Request) BoundFields

func (wsr *Request) BoundFields() types.StringSet

BoundFields returns the name of all of the names on the RequestBody that were explicitly set by the query/path parameter binding process.

func (*Request) HasFrameworkErrors

func (wsr *Request) HasFrameworkErrors() bool

HasFrameworkErrors returns true if one or more framework errors have been recorded.

func (*Request) RecordFieldAsBound

func (wsr *Request) RecordFieldAsBound(fieldName string)

RecordFieldAsBound is used to record the fact that a field on the RequestBody was explicitly set by the query/path parameter binding process.

func (*Request) WasFieldBound

func (wsr *Request) WasFieldBound(fieldName string) bool

WasFieldBound returns true if a field on the RequestBody was explicitly set by the query/path parameter binding process.

type Response

type Response struct {
	// An instruction that the HTTP status code should be set to this value (if the value is greater than 99). Generally
	// not set - the response writer will determine the correct status to use.
	HTTPStatus int

	// If the web service call resulted in data that should be written as the body of the HTTP response is stored in this field.
	// Application code must set this field explicitly.
	Body interface{}

	// All of the errors encountered while processing this request.
	Errors *ServiceErrors

	// Headers that should be set on the HTTP response.
	Headers map[string]string

	// If the type of response rendering is template based (e.g. using the XMLWs facility in template mode), this field
	// can be used to override any default templates or the template associated with the handler that created this response.
	Template string
}

Response contains data that is relevant to the rendering of the result of a web service request to an HTTP response. This type is agnostic of the format (JSON, XML etc) that is to be used to render the response.

func NewResponse

func NewResponse(errorFinder ServiceErrorFinder) *Response

NewResponse creates a valid but empty WsReponse with Errors structure initialised.

type ResponseWrapper

type ResponseWrapper interface {
	// WrapResponse takes the supplied body and errors and wraps them in a standardised data structure.
	WrapResponse(body interface{}, errors interface{}) interface{}
}

ResponseWrapper is implemented by components able to take the body from an Response and wrap it inside a container that will allow all responses to share a common structure.

type ResponseWriter

type ResponseWriter interface {
	// Write converts whatever data is present in the supplied state object to the HTTP output stream associated
	// with the current web service request.
	Write(ctx context.Context, state *ProcessState, outcome Outcome) error
}

ResponseWriter is implemented by components able write the result of a web service call to an HTTP response.

type ServiceErrorCategory

type ServiceErrorCategory int

ServiceErrorCategory indicates the broad 'type' of a service error, used to determine the correct HTTP status code to use.

func CodeToCategory

func CodeToCategory(c string) (ServiceErrorCategory, error)

CodeToCategory takes the short form of a category's name (its first letter, capitialised) an maps that to a ServiceErrorCategory

type ServiceErrorConsumer

type ServiceErrorConsumer interface {
	// ProvideErrorFinder receives a ServiceErrorFinder
	ProvideErrorFinder(finder ServiceErrorFinder)
}

ServiceErrorConsumer is implemented by components that require a ServiceErrorFinder to be injected into them

type ServiceErrorFinder

type ServiceErrorFinder interface {
	//Find takes a code and returns the message and category for that error. Behaviour undefined if code is not
	// recognised.
	Find(code string) *CategorisedError
}

ServiceErrorFinder is implemented by a component that is able to find a message and error category given the code for an error

type ServiceErrors

type ServiceErrors struct {
	// All services found, in the order in which they occurred.
	Errors []CategorisedError

	// An externally computed HTTP status code that reflects the mix of errors in this structure.
	HTTPStatus int

	// A component able to find additional information about error from that error's unique code.
	ErrorFinder ServiceErrorFinder
}

ServiceErrors is a structure that records each of the errors found during the processing of a request.

func (*ServiceErrors) AddError

func (se *ServiceErrors) AddError(e *CategorisedError)

AddError records the supplied error.

func (*ServiceErrors) AddNewError

func (se *ServiceErrors) AddNewError(category ServiceErrorCategory, label string, message string)

AddNewError creates a new CategorisedError from the supplied information and captures it.

func (*ServiceErrors) AddPredefinedError

func (se *ServiceErrors) AddPredefinedError(code string, field ...string) error

AddPredefinedError creates a CategorisedError by looking up the supplied code and records that error. If the variadic field parameter is supplied, the created error will be associated with that field name.

func (*ServiceErrors) HasErrors

func (se *ServiceErrors) HasErrors() bool

HasErrors returns true if one or more errors have been encountered and recorded.

type Unmarshaller

type Unmarshaller interface {
	// Unmarshall deserialises an HTTP request body and converts it to a struct.
	Unmarshall(ctx context.Context, req *http.Request, wsReq *Request) error
}

Unmarshaller is implemented by components that are able to convert an HTTP request body into a struct.

Directories

Path Synopsis
Package handler provides the types used to coordinate the processing of a web service request.
Package handler provides the types used to coordinate the processing of a web service request.
Package json defines types that are specific to handling web service requests and responses as JSON.
Package json defines types that are specific to handling web service requests and responses as JSON.
Package xml defines types that are specific to handling web service requests and responses as XML.
Package xml defines types that are specific to handling web service requests and responses as XML.

Jump to

Keyboard shortcuts

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