go-json-spec-handler: github.com/derekdowling/go-json-spec-handler Index | Files | Directories

package jsh

import "github.com/derekdowling/go-json-spec-handler"

Package jsh (JSON API Specification Handler) makes it easy to parse JSON API requests and send responses that match the JSON API Specification: http://jsonapi.org/ from your server.

For a request client, see: jsc: https://godoc.org/github.com/derekdowling/go-json-spec-handler/client

For a full http.Handler API builder see jshapi: https://godoc.org/github.com/derekdowling/go-json-spec-handler/jsh-api

Index

Package Files

document.go error.go jsh.go link.go list.go object.go parser.go relationship.go response.go test_util.go

Constants

const (
    // ContentType is the data encoding of choice for HTTP Request and Response Headers
    ContentType = "application/vnd.api+json"
)
const JSONAPIVersion = "1.1"

JSONAPIVersion is version of JSON API Spec that is currently compatible: http://jsonapi.org/format/1.1/

const MaxContentLength int64 = 10 << 20

MaxContentLength is 10MB https://github.com/golang/go/blob/abb3c0618b658a41bf91a087f1737412e93ff6d9/src/pkg/net/http/request.go#L617

Variables

var DefaultErrorDetail = "Request failed, something went wrong."

DefaultError can be customized in order to provide a more customized error Detail message when an Internal Server Error occurs. Optionally, you can modify a returned jsh.Error before sending it as a response as well.

var DefaultErrorTitle = "Internal Server Error"

DefaultTitle can be customized to provide a more customized ISE Title

var IncludeJSONAPIVersion = true

IncludeJSONAPIVersion is an option that allows consumers to include/remove the `jsonapi` top-level member from server responses.

func CreateReadCloser Uses

func CreateReadCloser(data []byte) io.ReadCloser

CreateReadCloser is a helper function for dealing with creating HTTP requests

type Document Uses

type Document struct {
    Data List `json:"data"`
    // Object   *Object     `json:"-"`
    Errors   ErrorList   `json:"errors,omitempty"`
    Links    *Links      `json:"links,omitempty"`
    Included []*Object   `json:"included,omitempty"`
    Meta     interface{} `json:"meta,omitempty"`
    JSONAPI  *JSONAPI    `json:"jsonapi,omitempty"`
    // Status is an HTTP Status Code
    Status int `json:"-"`
    // DataMode to enforce for the document
    Mode DocumentMode `json:"-"`
    // contains filtered or unexported fields
}

Document represents a top level JSON formatted Document. Refer to the JSON API Specification for a full descriptor of each attribute: http://jsonapi.org/format/#document-structure

func Build Uses

func Build(payload Sendable) *Document

Build creates a Sendable Document with the provided sendable payload, either Data or errors. Build also assumes you've already validated your data with .Validate() so it should be used carefully.

func New Uses

func New() *Document

New instantiates a new JSON Document object.

func Ok Uses

func Ok() *Document

Ok makes it simple to return a 200 OK response via jsh:

jsh.SendDocument(w, r, jsh.Ok())

func ParseDoc Uses

func ParseDoc(r *http.Request, mode DocumentMode) (*Document, *Error)

ParseDoc parses and returns a top level jsh.Document. In most cases, using "ParseList" or "ParseObject" is preferable.

func (*Document) AddError Uses

func (d *Document) AddError(newErr *Error) *Error

AddError adds an error to the Document. It will also set the document Mode to "ErrorMode" if not done so already.

func (*Document) AddObject Uses

func (d *Document) AddObject(object *Object) *Error

AddObject adds another object to the JSON Document after validating it.

func (*Document) Error Uses

func (d *Document) Error() string

func (*Document) First Uses

func (d *Document) First() *Object

First will return the first object from the document data if possible.

func (*Document) HasData Uses

func (d *Document) HasData() bool

HasData will return true if the JSON document's Data field is set

func (*Document) HasErrors Uses

func (d *Document) HasErrors() bool

HasErrors will return true if the Errors attribute is not nil.

func (*Document) MarshalJSON Uses

func (d *Document) MarshalJSON() ([]byte, error)

MarshalJSON handles the custom serialization case caused by case where the "data" element of a document might be either a single resource object, or a collection of them.

func (*Document) Validate Uses

func (d *Document) Validate(r *http.Request, isResponse bool) *Error

Validate performs document level checks against the JSONAPI specification. It is assumed that if this call returns without an error, your document is valid and can be sent as a request or response.

type DocumentMode Uses

type DocumentMode int

DocumentMode allows different specification settings to be enforced based on the specified mode.

const (
    // ObjectMode enforces fetch request/response specifications
    ObjectMode DocumentMode = iota
    // ListMode enforces listing request/response specifications
    ListMode
    // ErrorMode enforces error response specifications
    ErrorMode
)

type Error Uses

type Error struct {
    Title  string `json:"title"`
    Detail string `json:"detail"`
    Status int    `json:"status,string"`
    Source struct {
        Pointer string `json:"pointer"`
    }   `json:"source"`
    ISE string `json:"-"`
}

Error consists of a number of contextual attributes to make conveying certain error type simpler as per the JSON API specification: http://jsonapi.org/format/#error-objects

error := &jsh.Error{
	Title: "Authentication Failure",
	Detail: "Category 4 Username Failure",
	Status: 401
}

jsh.Send(w, r, error)

func ISE Uses

func ISE(internalMessage string) *Error

ISE is a convenience function for creating a ready-to-go Internal Service Error response. The message you pass in is set to the ErrorObject.ISE attribute so you can gracefully log ISE's internally before sending them.

func InputError Uses

func InputError(msg string, attribute string) *Error

InputError creates a properly formatted HTTP Status 422 error with an appropriate user safe message. The parameter "attribute" will format err.Source.Pointer to be "/data/attributes/<attribute>".

func NotFound Uses

func NotFound(resourceType string, id string) *Error

NotFound returns a 404 formatted error

func Send Uses

func Send(w http.ResponseWriter, r *http.Request, payload Sendable) *Error

Send will return a JSON payload to the requestor. If the payload response validation fails, it will send an appropriate error to the requestor and will return the error

func SendDocument Uses

func SendDocument(w http.ResponseWriter, r *http.Request, document *Document) *Error

SendDocument handles sending a fully prepared JSON Document. This is useful if you require custom validation or additional build steps before sending.

SendJSON is designed to always send a response, but will also return the last error it encountered to help with debugging in the event of an Internal Server Error.

func SpecificationError Uses

func SpecificationError(detail string) *Error

SpecificationError is used whenever the Client violates the JSON API Spec

func (*Error) Error Uses

func (e *Error) Error() string

Error will print an internal server error if set, or default back to the SafeError() format if not. As usual, err.Error() should not be considered safe for presentation to the end user, use err.SafeError() instead.

func (*Error) StatusCode Uses

func (e *Error) StatusCode() int

StatusCode (HTTP) for the error. Defaults to 0.

func (*Error) Validate Uses

func (e *Error) Validate(r *http.Request, response bool) *Error

Validate ensures that the an error meets all JSON API criteria.

type ErrorList Uses

type ErrorList []*Error

ErrorList is wraps an Error Array so that it can implement Sendable

func (ErrorList) Error Uses

func (e ErrorList) Error() string

Fulfills the default error interface

func (ErrorList) StatusCode Uses

func (e ErrorList) StatusCode() int

StatusCode (HTTP) of the first error in the list. Defaults to 0 if the list is empty or one has not yet been set for the first error.

func (ErrorList) Validate Uses

func (e ErrorList) Validate(r *http.Request, response bool) *Error

Validate checks all errors within the list to ensure that they are valid

type ErrorType Uses

type ErrorType interface {
    // Error returns a formatted error and allows it to conform to the stdErr
    // interface.
    Error() string
    // Validate checks that the error is valid in the context of JSONAPI
    Validate(r *http.Request, response bool) *Error
    // StatusCode returns the first encountered HTTP Status Code for the error type.
    // Returns 0 if none is set.
    StatusCode() int
}

ErrorType represents the common interface requirements that libraries may specify if they would like to accept either a single error or a list.

type JSONAPI Uses

type JSONAPI struct {
    Version string `json:"version"`
}

JSONAPI is the top-level member of a JSONAPI document that includes the server compatible version of the JSONAPI specification.

type Link struct {
    HREF string                 `json:"href,omitempty"`
    Meta map[string]interface{} `json:"meta,omitempty"`
}

Link is a resource link that can encode as a string or as an object as per the JSON API specification.

func NewLink(href string) *Link

NewLink creates a new link encoded as a string.

func NewMetaLink(href string, meta map[string]interface{}) *Link

NewMetaLink creates a new link with metadata encoded as an object.

func (*Link) MarshalJSON Uses

func (l *Link) MarshalJSON() ([]byte, error)

MarshalJSON implements the Marshaler interface for Link.

func (*Link) UnmarshalJSON Uses

func (l *Link) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the Unmarshaler interface for Link.

type Links struct {
    Self    *Link `json:"self,omitempty"`
    Related *Link `json:"related,omitempty"`
}

Links is a top-level document field

type List Uses

type List []*Object

List is just a wrapper around an object array that implements Sendable

func ParseList Uses

func ParseList(r *http.Request) (List, *Error)

ParseList validates the HTTP request and returns a resulting list of objects parsed from the request Body. Use just like ParseObject.

func (*List) UnmarshalJSON Uses

func (list *List) UnmarshalJSON(rawData []byte) error

UnmarshalJSON allows us to manually decode a list via the json.Unmarshaler interface.

func (List) Validate Uses

func (list List) Validate(r *http.Request, response bool) *Error

Validate ensures that List is JSON API compatible.

type Object Uses

type Object struct {
    Type          string                   `json:"type" valid:"required"`
    ID            string                   `json:"id"`
    Attributes    json.RawMessage          `json:"attributes,omitempty"`
    Links         map[string]*Link         `json:"links,omitempty"`
    Relationships map[string]*Relationship `json:"relationships,omitempty"`
    Meta          map[string]interface{}   `json:"meta,omitempty"`
    // Status is the HTTP Status Code that should be associated with the object
    // when it is sent.
    Status int `json:"-"`
}

Object represents the default JSON spec for objects

func NewObject Uses

func NewObject(id string, resourceType string, attributes interface{}) (*Object, *Error)

NewObject prepares a new JSON Object for an API response. Whatever is provided as attributes will be marshalled to JSON.

func ParseObject Uses

func ParseObject(r *http.Request) (*Object, *Error)

ParseObject validates the HTTP request and returns a JSON object for a given io.ReadCloser containing a raw JSON payload. Here's an example of how to use it as part of your full flow.

func Handler(w http.ResponseWriter, r *http.Request) {
	obj, error := jsh.ParseObject(r)
	if error != nil {
		// log your error
		err := jsh.Send(w, r, error)
		return
	}

	yourType := &YourType{}

	err := object.Unmarshal("yourtype", &yourType)
	if err != nil {
		err := jsh.Send(w, r, err)
		return
	}

	yourType.ID = obj.ID
	// do business logic

	err := object.Marshal(yourType)
	if err != nil {
		// log error
		err := jsh.Send(w, r, err)
		return
	}

	err := jsh.Send(w, r, object)
}

func (*Object) Marshal Uses

func (o *Object) Marshal(attributes interface{}) *Error

Marshal allows you to load a modified payload back into an object to preserve all of the data it has.

func (*Object) String Uses

func (o *Object) String() string

String prints a formatted string representation of the object

func (*Object) Unmarshal Uses

func (o *Object) Unmarshal(resourceType string, target interface{}) ErrorList

Unmarshal puts an Object's Attributes into a more useful target resourceType defined by the user. A correct object resourceType specified must also be provided otherwise an error is returned to prevent hard to track down situations.

Optionally, used https://github.com/go-validator/validator for request input validation. Simply define your struct with valid input tags:

struct {
	Username string `json:"username" valid:"required,alphanum"`
}

As the final action, the Unmarshal function will run govalidator on the unmarshal result. If the validator fails, a Sendable error response of HTTP Status 422 will be returned containing each validation error with a populated Error.Source.Pointer specifying each struct attribute that failed. In this case, all you need to do is:

errors := obj.Unmarshal("mytype", &myType)
if errors != nil {
	// log errors via error.ISE
	jsh.Send(w, r, errors)
}

func (*Object) Validate Uses

func (o *Object) Validate(r *http.Request, response bool) *Error

Validate ensures that an object is JSON API compatible. Has a side effect of also setting the Object's Status attribute to be used as the Response HTTP Code if one has not already been set.

type Parser Uses

type Parser struct {
    Method  string
    Headers http.Header
}

Parser is an abstraction layer that helps to support parsing JSON payload from many types of sources, and allows other libraries to leverage this if desired.

func NewParser Uses

func NewParser(request *http.Request) *Parser

NewParser creates a parser from an http.Request

func (*Parser) Document Uses

func (p *Parser) Document(payload io.ReadCloser, mode DocumentMode) (*Document, *Error)

Document returns a single JSON data object from the parser. In the process it will also validate any data objects against the JSON API.

type Relationship Uses

type Relationship struct {
    Links *Links                 `json:"links,omitempty"`
    Data  ResourceLinkage        `json:"data,omitempty"`
    Meta  map[string]interface{} `json:"meta,omitempty"`
}

Relationship represents a reference from the resource object in which it's defined to other resource objects.

type ResourceIdentifier Uses

type ResourceIdentifier struct {
    Type string `json:"type" valid:"required"`
    ID   string `json:"id" valid:"required"`
}

ResourceIdentifier identifies an individual resource.

type ResourceLinkage Uses

type ResourceLinkage []*ResourceIdentifier

ResourceLinkage is a typedef around a slice of resource identifiers. This allows us to implement a custom UnmarshalJSON.

func (*ResourceLinkage) UnmarshalJSON Uses

func (rl *ResourceLinkage) UnmarshalJSON(data []byte) error

UnmarshalJSON allows us to manually decode a the resource linkage via the json.Unmarshaler interface.

type Sendable Uses

type Sendable interface {
    Validate(r *http.Request, response bool) *Error
}

Sendable implements functions that allows different response types to produce a sendable JSON Response format

Directories

PathSynopsis
clientPackage jsc (JSON Specification Client) is an http client that makes sending HTTP requests that match the JSON Specification: http://jsonapi.org/ simple.
goji2-logger
jsh-apiPackage jshapi is a http.Handler compatible wrapper that makes building JSON API resource handlers easy.
jsh-api/storePackage store is a collection of composable interfaces that are can be implemented in order to build a storage driver

Package jsh imports 10 packages (graph) and is imported by 9 packages. Updated 2017-07-09. Refresh now. Tools for package owners.