diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ec8c5ab..c89a3c23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ #Changelog +###Gin 0.5 (Aug 21, 2014) + +- [NEW] Content Negotiation + + ###Gin 0.4 (Aug 21, 2014) - [NEW] Development mode diff --git a/context.go b/context.go index 294d1cce..178379b8 100644 --- a/context.go +++ b/context.go @@ -67,6 +67,7 @@ type Context struct { Engine *Engine handlers []HandlerFunc index int8 + accepted []string } /************************************/ @@ -81,6 +82,7 @@ func (engine *Engine) createContext(w http.ResponseWriter, req *http.Request, pa c.handlers = handlers c.Keys = nil c.index = -1 + c.accepted = nil c.Errors = c.Errors[0:0] return c } @@ -275,3 +277,64 @@ func (c *Context) Data(code int, contentType string, data []byte) { func (c *Context) File(filepath string) { http.ServeFile(c.Writer, c.Request, filepath) } + +/************************************/ +/******** CONTENT NEGOTIATION *******/ +/************************************/ + +type Negotiate struct { + Offered []string + HTMLPath string + HTMLData interface{} + JSONData interface{} + XMLData interface{} + Data interface{} +} + +func (c *Context) Negotiate(code int, config Negotiate) { + switch c.NegotiateFormat(config.Offered...) { + case MIMEJSON: + data := chooseData(config.JSONData, config.Data) + c.JSON(code, data) + + case MIMEHTML: + data := chooseData(config.HTMLData, config.Data) + if len(config.HTMLPath) == 0 { + panic("negotiate config is wrong. html path is needed") + } + c.HTML(code, config.HTMLPath, data) + + case MIMEXML: + data := chooseData(config.XMLData, config.Data) + c.XML(code, data) + + default: + c.Fail(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) + } +} + +func (c *Context) NegotiateFormat(offered ...string) string { + if len(offered) == 0 { + panic("you must provide at least one offer") + } + if c.accepted == nil { + c.accepted = parseAccept(c.Request.Header.Get("Accept")) + } + if len(c.accepted) == 0 { + return offered[0] + + } else { + for _, accepted := range c.accepted { + for _, offert := range offered { + if accepted == offert { + return offert + } + } + } + return "" + } +} + +func (c *Context) SetAccepted(formats ...string) { + c.accepted = formats +} diff --git a/utils.go b/utils.go index f58097a4..fee5728b 100644 --- a/utils.go +++ b/utils.go @@ -8,6 +8,7 @@ import ( "encoding/xml" "reflect" "runtime" + "strings" ) type H map[string]interface{} @@ -45,6 +46,29 @@ func filterFlags(content string) string { return content } +func chooseData(custom, wildcard interface{}) interface{} { + if custom == nil { + if wildcard == nil { + panic("negotiation config is invalid") + } + return wildcard + } + return custom +} + +func parseAccept(accept string) []string { + parts := strings.Split(accept, ",") + for i, part := range parts { + index := strings.IndexByte(part, ';') + if index >= 0 { + part = part[0:index] + } + part = strings.TrimSpace(part) + parts[i] = part + } + return parts +} + func funcName(f interface{}) string { return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() }