From ffea7e88a28d2809563bd366944ff71a3e217c1e Mon Sep 17 00:00:00 2001 From: Manu Mtz-Almeida Date: Sat, 30 Aug 2014 22:22:57 +0200 Subject: [PATCH 1/3] Working on content type negotiation API --- CHANGELOG.md | 5 ++++ context.go | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++ utils.go | 26 +++++++++++++++++++ 3 files changed, 104 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ec8c5a..c89a3c2 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 294d1cc..570314b 100644 --- a/context.go +++ b/context.go @@ -67,6 +67,7 @@ type Context struct { Engine *Engine handlers []HandlerFunc index int8 + accepted []string } /************************************/ @@ -275,3 +276,75 @@ 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 + Data interface{} + JsonData interface{} + XMLData interface{} + HTMLData interface{} + HTMLPath string +} + +func (c *Context) Negotiate2(code int, config Negotiate) { + result := c.NegotiateFormat(config.Offered...) + switch result { + case MIMEJSON: + c.JSON(code, config.Data) + + case MIMEHTML: + name := config.HTMLPath + c.HTML(code, name, config.Data) + + case MIMEXML: + c.XML(code, config.Data) + default: + c.Fail(400, errors.New("m")) + } +} + +func (c *Context) Negotiate(code int, config map[string]interface{}, offerts ...string) { + result := c.NegotiateFormat(offerts...) + switch result { + case MIMEJSON: + data := readData("json.data", config) + c.JSON(code, data) + + case MIMEHTML: + data := readData("html.data", config) + name := config["html.path"].(string) + c.HTML(code, name, data) + + case MIMEXML: + data := readData("xml.data", config) + c.XML(code, data) + default: + c.Fail(400, errors.New("m")) + } +} + +func (c *Context) NegotiateFormat(offered ...string) string { + 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 f58097a..96d403f 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,31 @@ func filterFlags(content string) string { return content } +func readData(key string, config map[string]interface{}) interface{} { + data, ok := config[key] + if ok { + return data + } + data, ok = config["*.data"] + if !ok { + panic("negotiation config is invalid") + } + return data +} + +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() } From 275bdc194ed699776c960e33a80959d1c2ea9570 Mon Sep 17 00:00:00 2001 From: Manu Mtz-Almeida Date: Sun, 31 Aug 2014 18:28:18 +0200 Subject: [PATCH 2/3] Fixes Content.Negotiate API --- context.go | 42 +++++++++++++++--------------------------- utils.go | 16 +++++++--------- 2 files changed, 22 insertions(+), 36 deletions(-) diff --git a/context.go b/context.go index 570314b..123d257 100644 --- a/context.go +++ b/context.go @@ -82,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 } @@ -280,47 +281,34 @@ func (c *Context) File(filepath string) { /************************************/ /******** CONTENT NEGOTIATION *******/ /************************************/ + type Negotiate struct { Offered []string - Data interface{} - JsonData interface{} - XMLData interface{} - HTMLData interface{} HTMLPath string + HTMLData interface{} + JSONData interface{} + XMLData interface{} + Data interface{} } -func (c *Context) Negotiate2(code int, config Negotiate) { +func (c *Context) Negotiate(code int, config Negotiate) { result := c.NegotiateFormat(config.Offered...) switch result { case MIMEJSON: - c.JSON(code, config.Data) - - case MIMEHTML: - name := config.HTMLPath - c.HTML(code, name, config.Data) - - case MIMEXML: - c.XML(code, config.Data) - default: - c.Fail(400, errors.New("m")) - } -} - -func (c *Context) Negotiate(code int, config map[string]interface{}, offerts ...string) { - result := c.NegotiateFormat(offerts...) - switch result { - case MIMEJSON: - data := readData("json.data", config) + data := chooseData(config.JSONData, config.Data) c.JSON(code, data) case MIMEHTML: - data := readData("html.data", config) - name := config["html.path"].(string) - c.HTML(code, name, data) + 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 := readData("xml.data", config) + data := chooseData(config.XMLData, config.Data) c.XML(code, data) + default: c.Fail(400, errors.New("m")) } diff --git a/utils.go b/utils.go index 96d403f..fee5728 100644 --- a/utils.go +++ b/utils.go @@ -46,16 +46,14 @@ func filterFlags(content string) string { return content } -func readData(key string, config map[string]interface{}) interface{} { - data, ok := config[key] - if ok { - return data +func chooseData(custom, wildcard interface{}) interface{} { + if custom == nil { + if wildcard == nil { + panic("negotiation config is invalid") + } + return wildcard } - data, ok = config["*.data"] - if !ok { - panic("negotiation config is invalid") - } - return data + return custom } func parseAccept(accept string) []string { From 012c935a465079ae7edc75cce9100a13b3d2cd8e Mon Sep 17 00:00:00 2001 From: Manu Mtz-Almeida Date: Sun, 31 Aug 2014 18:41:11 +0200 Subject: [PATCH 3/3] Better errors in Context.Negotiation --- context.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/context.go b/context.go index 123d257..178379b 100644 --- a/context.go +++ b/context.go @@ -292,8 +292,7 @@ type Negotiate struct { } func (c *Context) Negotiate(code int, config Negotiate) { - result := c.NegotiateFormat(config.Offered...) - switch result { + switch c.NegotiateFormat(config.Offered...) { case MIMEJSON: data := chooseData(config.JSONData, config.Data) c.JSON(code, data) @@ -310,11 +309,14 @@ func (c *Context) Negotiate(code int, config Negotiate) { c.XML(code, data) default: - c.Fail(400, errors.New("m")) + 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")) }