Top Banner
RESTful Web Applications with Google Go
44

RESTful Web Applications with Google Go

Dec 05, 2014

Download

Software

Frank Müller

 
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
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