restful web applications with google go

44
RESTful Web Applications with Google Go

Upload: frank-mueller

Post on 05-Dec-2014

564 views

Category:

Software


5 download

DESCRIPTION

 

TRANSCRIPT

Page 1: RESTful Web Applications with Google Go

RESTful Web Applications with Google Go

Page 2: RESTful Web Applications with Google Go

Frank Müller

Oldenburg / Germany Released Summer 1965

Software Engineer Author

!

[email protected] blog.tideland.biz

@themue github.com/tideland

Page 3: RESTful Web Applications with Google Go

Goals

• Usage of HTTP / HTTPS

• Multiplexing based on path containing functional domain, resource, and possible resource id

• List of multiple handles to support generic tasks like authentication and authorization

• Mapping of HTTP methods to CRUD operations

• Major data is JSON, but also XML and templates

Page 4: RESTful Web Applications with Google Go

Google Go

Page 5: RESTful Web Applications with Google Go

Go HTTP Package

• Simple

• Types implementing http.Handler interface or functions with a defined signature for handling

• Integrated server able to handle HTTP and HTTPS

• Not very convenient

Page 6: RESTful Web Applications with Google Go

Go HTTP Package - Handler

type MyHandler struct{} !// Implementing http.Handler interface. func (mh *MyHandler) ServeHTTP( w http.ResponseWriter, r *http.Request) { w.Header().Set(”Content-Type”, ”text/plain”) w.WriteHeader(http.StatusOK) fmt.Fprintln(w, ”Hello, Go User Group!”) }

Page 7: RESTful Web Applications with Google Go

Go HTTP Package - Main

!func main() { // Register handler for path. http.Handle(”/myHandler”, &MyHandler{}) ! // Start server on port 8080. log.Fatal(http.ListenAndServe(”:8080”, nil)) }

Page 8: RESTful Web Applications with Google Go

❝Simple tasks can be done using the standard library, but own powerful

packages are easy to create.

–Gopher

Page 9: RESTful Web Applications with Google Go

RESTful Web Multiplexer

Page 10: RESTful Web Applications with Google Go

Multiplexer

• Go default uses a prefix based pattern

• Our RWM maps based on domain and resource

• Request and response wrapped into convenient context

• Fallback to a default handler

Page 11: RESTful Web Applications with Google Go

Multiplexer - Type

// RESTfulWebMultiplexer is our own multiplexer. type RESTfulWebMultiplexer struct { mapping domains … } !// AddHandler adds a handler based on domain and resource. func (mux * RESTfulWebMultiplexer) AddHandler( domain, resource string, h ResourceHandler) error { … }

Page 12: RESTful Web Applications with Google Go

Multiplexer - Interface Method

// ServeHTTP implements the handler interface. func (mux * RESTfulWebMultiplexer) ServeHTTP( w http.ResponseWriter, r *http.Request) { ctx := newContext(mux, w, r) if err := mux.mapping.handle(ctx); err != nil { … } }

Page 13: RESTful Web Applications with Google Go

Multiplexer - Main

!func main() { // Create multiplexer and add handlers. mux := NewRESTfulWebMultiplexer() ! mux.AddHandler(”content”, ”blog”, NewBlogHandler()) … ! // Start server with our multiplexer on port 8080. log.Fatal(http.ListenAndServe(”:8080”, mux)) }

Page 14: RESTful Web Applications with Google Go

❝Own multiplexers make HTTP server

more flexible.

–Gopher

Page 15: RESTful Web Applications with Google Go

Multiplexer - Domains

// domains maps domains to their resources. type domains map[string]resources !// handle retrieves the resources for the context domain and // lets them handle the context. func (d domains) handle( ctx *RequestContext) error { resources, ok := d[ctx.Domain] if !ok { resources = d[ctx.Mux.DefaultDomain()] } // Continue handling. return resources.handle(ctx) }

Page 16: RESTful Web Applications with Google Go

Multiplexer - Resources

// resources maps resources to their handler lists. type resources map[string]handlers !// handle retrieves the handlers for the context resource and lets // them handle the context. func (r resources) handle( ctx *RequestContext) error { handlers, ok := r[ctx.Resource] if !ok { handlers = r[ctx.Mux.DefaultResource(ctx.Domain)] } // Continue handling. return handlers.handle(ctx) }

Page 17: RESTful Web Applications with Google Go

Multiplexer - Handlers

// handlers chains all handlers for one resource. type handlers []ResourceHandler !// handle lets all handlers handle the context. func (h handlers) handle( ctx *RequestContext) error { for _, handler := range h { ok, err := ctx.Mux.dispatch(ctx, handler) if err != nil { return err } // Handler tells to stop, but w/o error. if !ok { return nil } } return nil }

Page 18: RESTful Web Applications with Google Go

❝Use my simple type system for small

types with useful methods.

–Gopher

Page 19: RESTful Web Applications with Google Go

Handle your resources

Page 20: RESTful Web Applications with Google Go

Resource Handler

• Basic interface for initialization and read operation

• Additional interfaces for create, update, and delete operations

• Dispatcher to map HTTP methods

Page 21: RESTful Web Applications with Google Go

Resource Handler - Base Interface

// ResourceHandler defines the base interface. It handles the // HTTP GET method with Read(). type ResourceHandler interface { // Init is called after registration of the handler. Init(domain, resource string) error ! // Read is called if the HTTP method is GET. Read(ctx *Context) (bool, error) }

Page 22: RESTful Web Applications with Google Go

Resource Handler - Create Interface

// CreateResourceHandler defines the interface to additionally // handle the HTTP POST with Create(). type CreateResourceHandler interface { // Create is called if the HTTP method is POST. Create(ctx *Context) (bool, error) }

Page 23: RESTful Web Applications with Google Go

Resource Handler - Dispatch

// dispatch maps HTTP methods to handler function calls. func (mux *RESTfulWebMultiplexer) dispatch( ctx *Context, h ResourceHandler) (bool, error) { switch ctx.Request.Method { case ”GET”: return h.Read(ctx) case ”POST”: if ch, ok := h.(CreateResourceHandler); ok { return ch.Create(ctx) } return false, errors.New(”handler cannot process POST”) case … } return false, errors.New(”invalid HTTP method”) }

Page 24: RESTful Web Applications with Google Go

❝Small interfaces and type assertions

are a powerful combination.

–Gopher

Page 25: RESTful Web Applications with Google Go

See the context

Page 26: RESTful Web Applications with Google Go

Context

• Simple wrapper for request and response

• Provides information about domain, resource and id

• Also provides information about stuff like accepted content types and languages

• Allows simpler reading and writing of JSON etc.

Page 27: RESTful Web Applications with Google Go

Context - Type

// Context encapsulates all needed data for handling a request. type Context struct { Mux *RESTfulWebMultiplexer Writer http.ResponseWriter Request *http.Request Domain, Resource, ResourceId string } !// newContext creates a new context and parses the URL path. func newContext( mux *RESTfulWebMultiplexer, w http.ResponseWriter, r *http.Request) *Context { … }

Page 28: RESTful Web Applications with Google Go

Context - Simple Request Analysis

// accepts checks if the requestor accepts a content type. func (ctx *Context) accepts(ct string) bool { accept := ctx.Request.Header.Get(”Accept”) return strings.Contains(accept, ct) } !// AcceptsJSON checks if the requestor accepts JSON as // a content type. func (ctx *Context) AcceptsJSON() bool { return ctx.accepts(”application/json”) }

Page 29: RESTful Web Applications with Google Go

Context - Typical Operations

// Redirect to a domain, resource and resource id (optional). func (ctx *Context) Redirect( domain, resource, resourceId string) { url := ctx.Mux.BasePath() + domain + ”/” + resource if resourceId != ”” { url += ”/” + resourceId } ctx.Writer.Header().Set(”Location”, url) ctx.Writer.WriteHeader(http.StatusMovedPermanently) }

Page 30: RESTful Web Applications with Google Go

❝Public fields are not evil as long as

the data is not shared.

–Gopher

Page 31: RESTful Web Applications with Google Go

JSON Marshaling

• Go likes JSON

• Really! (scnr)

• Automatically, controlled, and manually

Page 32: RESTful Web Applications with Google Go

JSON - Standard

// Public fields will be marshaled. type Demo struct { FieldA string FieldB int FieldC *OtherStruct fieldX bool // No, you won’t see me. } !demo := &demo{ … } !// b contains the marshaled demo struct as []byte. b, err := json.Marshal(demo)

Page 33: RESTful Web Applications with Google Go

JSON - Controlled

// Control with field tags. type AnotherDemo struct { FieldA string `json:”-”` // Ignore. FieldB int `json:”OtherName”` // Change name. FieldC float64 `json:”,string”` // As string. FieldD bool `json:”,omitempty”`// Ignore if empty. FieldE string // As usual. fieldX int // Still ignored. }

Page 34: RESTful Web Applications with Google Go

JSON - Manually

// User has to care for it. type StillADemo struct { fieldAstring fieldBint } !// MarshalJSON implements the Marshaler interface. func (d *StillADemo) MarshalJSON() ([]byte, error) { format := `{”First”: %q, ”Second”: %d}` json := fmt.Sprintf(format, d.fieldA, d.fieldB) return []byte(json), nil }

Page 35: RESTful Web Applications with Google Go

JSON - Integrate in Context

func (ctx *Context) RespondJSON( data interface{}, html bool) error { b, err := json.Marshal(data) if err != nil { return fmt.Errorf(”cannot respond JSON: %v”, err) } if html { var buf bytes.Buffer json.HTMLEscape(&buf, b) b = buf.Bytes() } ctx.Writer.Header().Set(”Content-Type”, ”application/json”) _, err = ctx.Writer.Write(b) return err }

Page 36: RESTful Web Applications with Google Go

❝My standard library provides powerful

encoding packages, also for XML, CSV, ASN.1, etc.

–Gopher

Page 37: RESTful Web Applications with Google Go

Scenario

Page 38: RESTful Web Applications with Google Go

Tags by Interest

Browser

Stat Handler

Content Handler

Tag Handler

Content Backend

Stat Backend

DB

GET /content/page/4711

GET /content/tags/interest Goroutines

Async Update

Page Requestgets HTML

JS Requestgets JSON

Page 39: RESTful Web Applications with Google Go

Stat Handler

func (h *StatHandler) Read(ctx *rwm.Context) (bool, error) { if ctx.ResourceId != ”” { // Backend handles update in background. statBackend.UpdatePage(ctx.ResourceId) } return true, nil }

Page 40: RESTful Web Applications with Google Go

Content Handler

func (h *ContentHandler) Read( ctx *rwm.Context) (bool, error) { var page *Page if ctx.ResourceId != ”” { page = contentBackend.Page(ctx.ResourceId) } else { page = contentBackend.Index() } if err := ctx.RespondTemplate(h.template, page); err != nil { return false, err } return true, nil }

Page 41: RESTful Web Applications with Google Go

Tag Handler

func (h *StatHandler) Read(ctx *rwm.Context) (bool, error) { var err error switch ctx.ResourceId { case ”interest”: tags := statBackend.TagsByInterest() if ctx.AcceptsJSON() { err = ctx.RespondJSON(tags, true) } else if ctx.AcceptsXML() { err = ctx.RespondXML(tags) } case … } … }

Page 42: RESTful Web Applications with Google Go

❝Enjoy Go, it’s lightweight, simple and

very productive.

–Gopher

Page 43: RESTful Web Applications with Google Go
Page 44: RESTful Web Applications with Google Go

❝Zitat hier eingeben.

–Christian BauerImages 123RFiStockphotoOwn Sources