rest-layer: github.com/rs/rest-layer/rest Index | Examples | Files

package rest

import "github.com/rs/rest-layer/rest"

Package rest is a `net/http` handler responsible for HTTP RESTful implementation for the REST Layer framework.

See http://github.com/rs/rest-layer for full REST Layer documentation.

Code:

var (
    // Define a user resource schema
    user = schema.Schema{
        Fields: schema.Fields{
            "id": {
                Required: true,
                // When a field is read-only, on default values or hooks can
                // set their value. The client can't change it.
                ReadOnly: true,
                // This is a field hook called when a new user is created.
                // The schema.NewID hook is a provided hook to generate a
                // unique id when no value is provided.
                OnInit: schema.NewID,
                // The Filterable and Sortable allows usage of filter and sort
                // on this field in requests.
                Filterable: true,
                Sortable:   true,
                Validator: &schema.String{
                    Regexp: "^[0-9a-f]{32}$",
                },
            },
            "created": {
                Required:   true,
                ReadOnly:   true,
                Filterable: true,
                Sortable:   true,
                OnInit:     schema.Now,
                Validator:  &schema.Time{},
            },
            "updated": {
                Required:   true,
                ReadOnly:   true,
                Filterable: true,
                Sortable:   true,
                OnInit:     schema.Now,
                // The OnUpdate hook is called when the item is edited. Here we use
                // provided Now hook which just return the current time.
                OnUpdate:  schema.Now,
                Validator: &schema.Time{},
            },
            // Define a name field as required with a string validator
            "name": {
                Required:   true,
                Filterable: true,
                Validator: &schema.String{
                    MaxLen: 150,
                },
            },
        },
    }

    // Define a post resource schema
    post = schema.Schema{
        Fields: schema.Fields{
            // schema.*Field are shortcuts for common fields (identical to users' same fields)
            "id":      schema.IDField,
            "created": schema.CreatedField,
            "updated": schema.UpdatedField,
            // Define a user field which references the user owning the post.
            // See bellow, the content of this field is enforced by the fact
            // that posts is a sub-resource of users.
            "user": {
                Required:   true,
                Filterable: true,
                Validator: &schema.Reference{
                    Path: "users",
                },
            },
            "public": {
                Filterable: true,
                Validator:  &schema.Bool{},
            },
            // Sub-documents are handled via a sub-schema
            "meta": {
                Schema: &schema.Schema{
                    Fields: schema.Fields{
                        "title": {
                            Required: true,
                            Validator: &schema.String{
                                MaxLen: 150,
                            },
                        },
                        "body": {
                            Validator: &schema.String{
                                MaxLen: 100000,
                            },
                        },
                    },
                },
            },
        },
    }
)

// Create a REST API root resource
index := resource.NewIndex()

// Add a resource on /users[/:user_id]
users := index.Bind("users", user, mem.NewHandler(), resource.Conf{
    // We allow all REST methods
    // (rest.ReadWrite is a shortcut for []rest.Mode{Create, Read, Update, Delete, List})
    AllowedModes: resource.ReadWrite,
})

// Bind a sub resource on /users/:user_id/posts[/:post_id]
// and reference the user on each post using the "user" field of the posts resource.
posts := users.Bind("posts", "user", post, mem.NewHandler(), resource.Conf{
    // Posts can only be read, created and deleted, not updated
    AllowedModes: []resource.Mode{resource.Read, resource.List, resource.Create, resource.Delete},
})

// Add a friendly alias to public posts
// (equivalent to /users/:user_id/posts?filter={"public":true})
posts.Alias("public", url.Values{"filter": []string{"{\"public\"=true}"}})

// Create API HTTP handler for the resource graph
api, err := rest.NewHandler(index)
if err != nil {
    log.Fatalf("Invalid API configuration: %s", err)
}

// Add cors support
h := cors.New(cors.Options{OptionsPassthrough: true}).Handler(api)

// Bind the API under /api/ path
http.Handle("/api/", http.StripPrefix("/api/", h))

// Serve it
log.Print("Serving API on http://localhost:8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
    log.Fatal(err)
}

Index

Examples

Package Files

doc.go errors.go handler.go method_delete.go method_get.go method_item_delete.go method_item_get.go method_item_options.go method_item_patch.go method_item_put.go method_options.go method_post.go resource_path.go response.go routing.go util.go

Variables

var (
    // ErrNotFound represents a 404 HTTP error.
    ErrNotFound = &Error{http.StatusNotFound, "Not Found", nil}
    // ErrUnauthorized represents a 401 HTTP error.
    ErrUnauthorized = &Error{http.StatusUnauthorized, "Unauthorized", nil}
    // ErrPreconditionFailed happens when a conditional request condition is not met.
    ErrPreconditionFailed = &Error{http.StatusPreconditionFailed, "Precondition Failed", nil}
    // ErrConflict happens when another thread or node modified the data concurrently
    // with our own thread in such a way we can't securely apply the requested changes.
    ErrConflict = &Error{http.StatusConflict, "Conflict", nil}
    // ErrInvalidMethod happens when the used HTTP method is not supported for this
    // resource.
    ErrInvalidMethod = &Error{http.StatusMethodNotAllowed, "Invalid Method", nil}
    // ErrClientClosedRequest is returned when the client closed the connection before
    // the server was able to finish processing the request.
    ErrClientClosedRequest = &Error{499, "Client Closed Request", nil}
    // ErrNotImplemented happens when a requested feature is not implemented.
    ErrNotImplemented = &Error{http.StatusNotImplemented, "Not Implemented", nil}
    // ErrGatewayTimeout is returned when the specified timeout for the request has been
    // reached before the server was able to process it.
    ErrGatewayTimeout = &Error{http.StatusGatewayTimeout, "Deadline Exceeded", nil}
    // ErrUnknown is thrown when the origine of the error can't be identified.
    ErrUnknown = &Error{520, "Unknown Error", nil}
)

func IndexFromContext Uses

func IndexFromContext(ctx context.Context) (resource.Index, bool)

IndexFromContext extracts the router from the given net/context

type DefaultResponseFormatter Uses

type DefaultResponseFormatter struct {
}

DefaultResponseFormatter provides a base response formatter to be used by default. This formatter can easily be extended or replaced by implementing ResponseFormatter interface and setting it on Handler.ResponseFormatter.

func (DefaultResponseFormatter) FormatError Uses

func (f DefaultResponseFormatter) FormatError(ctx context.Context, headers http.Header, err error, skipBody bool) (context.Context, interface{})

FormatError implements ResponseFormatter

func (DefaultResponseFormatter) FormatItem Uses

func (f DefaultResponseFormatter) FormatItem(ctx context.Context, headers http.Header, i *resource.Item, skipBody bool) (context.Context, interface{})

FormatItem implements ResponseFormatter

func (DefaultResponseFormatter) FormatList Uses

func (f DefaultResponseFormatter) FormatList(ctx context.Context, headers http.Header, l *resource.ItemList, skipBody bool) (context.Context, interface{})

FormatList implements ResponseFormatter

type DefaultResponseSender Uses

type DefaultResponseSender struct {
}

DefaultResponseSender provides a base response sender to be used by default. This sender can easily be extended or replaced by implementing ResponseSender interface and setting it on Handler.ResponseSender.

func (DefaultResponseSender) Send Uses

func (s DefaultResponseSender) Send(ctx context.Context, w http.ResponseWriter, status int, headers http.Header, body interface{})

Send sends headers with the given status and marshal the data in JSON

type Error Uses

type Error struct {
    // Code defines the error code to be used for the error and for the HTTP status
    Code int
    // Message is the error message
    Message string
    // Issues holds per fields errors if any
    Issues map[string][]interface{}
}

Error defines a REST error with optional per fields error details

func NewError Uses

func NewError(err error) *Error

NewError returns a rest.Error from an standard error.

If the the inputed error is recognized, the appropriate rest.Error is mapped.

func (*Error) Error Uses

func (e *Error) Error() string

Error returns the error as string

type Handler Uses

type Handler struct {
    // ResponseFormatter can be changed to extend the DefaultResponseFormatter
    ResponseFormatter ResponseFormatter
    // ResponseSender can be changed to extend the DefaultResponseSender
    ResponseSender ResponseSender
    // FallbackHandlerFunc is called when REST layer doesn't find a route for the request.
    // If not set, a 404 or 405 standard REST error is returned.
    FallbackHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request)
    // contains filtered or unexported fields
}

Handler is a net/http compatible handler used to serve the configured REST API

func NewHandler Uses

func NewHandler(i resource.Index) (*Handler, error)

NewHandler creates an new REST API HTTP handler with the specified resource index

func (*Handler) ServeHTTP Uses

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP handles requests as a http.Handler

func (*Handler) ServeHTTPC Uses

func (h *Handler) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request)

ServeHTTPC handles requests as a xhandler.HandlerC

type ResourcePath Uses

type ResourcePath []*ResourcePathComponent

ResourcePath is the list of ResourcePathComponent leading to the requested resource

func (ResourcePath) ParentsExist Uses

func (p ResourcePath) ParentsExist(ctx context.Context) error

ParentsExist checks if the each intermediate parents in the path exist and return either a ErrNotFound or an error returned by on of the intermediate resource.

func (ResourcePath) Path Uses

func (p ResourcePath) Path() string

Path returns the path to the resource to be used with resource.Root.GetResource

func (*ResourcePath) Prepend Uses

func (p *ResourcePath) Prepend(rsrc *resource.Resource, field string, value interface{})

Prepend add the given resource using the provided field and value as a "ghost" resource prefix to the resource path.

The effect will be a 404 error if the doesn't have an item with the id matching to the provided value.

This will also require that all subsequent resources in the path have this resource's "value" set on their "field" field.

Finally, all created resources at this path will also have this field and value set by default.

func (ResourcePath) Values Uses

func (p ResourcePath) Values() map[string]interface{}

Values returns all the key=value pairs defined by the resource path

type ResourcePathComponent Uses

type ResourcePathComponent struct {
    // Name is the endpoint name used to bind the resource
    Name string
    // Field is the resource's field used to filter targeted resource
    Field string
    // Value holds the resource's id value
    Value interface{}
    // Resource references the resource
    Resource *resource.Resource
}

ResourcePathComponent represents the path of resource and sub-resources of a given request's resource

type ResponseFormatter Uses

type ResponseFormatter interface {
    // FormatItem formats a single item in a format ready to be serialized by the ResponseSender
    FormatItem(ctx context.Context, headers http.Header, i *resource.Item, skipBody bool) (context.Context, interface{})
    // FormatList formats a list of items in a format ready to be serialized by the ResponseSender
    FormatList(ctx context.Context, headers http.Header, l *resource.ItemList, skipBody bool) (context.Context, interface{})
    // FormatError formats a REST formated error or a simple error in a format ready to be serialized by the ResponseSender
    FormatError(ctx context.Context, headers http.Header, err error, skipBody bool) (context.Context, interface{})
}

ResponseFormatter defines an interface responsible for formatting a the different types of response objects

type ResponseSender Uses

type ResponseSender interface {
    // Send serialize the body, sets the given headers and write everything to the provided response writer
    Send(ctx context.Context, w http.ResponseWriter, status int, headers http.Header, body interface{})
}

ResponseSender defines an interface responsible for serializing and sending the response to the http.ResponseWriter.

Code:

package main

import (
    "context"
    "net/http"

    "github.com/rs/rest-layer/resource"
    "github.com/rs/rest-layer/rest"
)

type myResponseFormatter struct {
    // Extending default response sender
    rest.DefaultResponseFormatter
}

// Add a wrapper around the list with pagination info
func (r myResponseFormatter) FormatList(ctx context.Context, headers http.Header, l *resource.ItemList, skipBody bool) (context.Context, interface{}) {
    ctx, data := r.DefaultResponseFormatter.FormatList(ctx, headers, l, skipBody)
    return ctx, map[string]interface{}{
        "meta": map[string]int{
            "total":  l.Total,
            "offset": l.Offset,
        },
        "list": data,
    }
}

func main() {
    index := resource.NewIndex()
    api, _ := rest.NewHandler(index)
    api.ResponseFormatter = myResponseFormatter{}
}

type RouteMatch Uses

type RouteMatch struct {
    // Method is the HTTP method used on the resource.
    Method string
    // ResourcePath is the list of intermediate resources followed by the targeted resource.
    // Each intermediate resource mutch match all the previous resource components of this path
    // and newly created resources will have their corresponding fields filled with resource
    // path information (resource.field => resource.value).
    ResourcePath ResourcePath
    // Params is the list of client provided parameters (thru query-string or alias).
    Params url.Values
}

RouteMatch represent a REST request's matched resource with the method to apply and its parameters

func FindRoute Uses

func FindRoute(index resource.Index, req *http.Request) (*RouteMatch, error)

FindRoute returns the REST route for the given request

func RouteFromContext Uses

func RouteFromContext(ctx context.Context) (*RouteMatch, bool)

RouteFromContext extracts the matched route from the given net/context

func (RouteMatch) Lookup Uses

func (r RouteMatch) Lookup() (*resource.Lookup, *Error)

Lookup builds a Lookup object from the matched route

func (RouteMatch) Release Uses

func (r RouteMatch) Release()

Release releases the route so it can be reused

func (RouteMatch) Resource Uses

func (r RouteMatch) Resource() *resource.Resource

Resource returns the last resource path's resource

func (RouteMatch) ResourceID Uses

func (r RouteMatch) ResourceID() interface{}

ResourceID returns the last resource path's resource id value if any.

If this method returns a non nil value, it means the route is an item request, otherwise it's a collection request.

Package rest imports 12 packages (graph). Updated 2017-03-28. Refresh now. Tools for package owners.