Comments (19)
Is there a way to register a renderer for a custom MediaType?
I want to use something like application/vnd.myapp.person.json;version=1.0.0
as the "preferred" json format and application/json
as a fallback.
from gin.
An API example:
func main() {
r := gin.Default()
r.GET("/hola", func(c *gin.Context) {
data := gin.H{"status": "ok"}
switch c.NegotiateFormat(gin.MIMEHTML, gin.MIMEJSON) {
case gin.MIMEHTML:
c.HTML(200, "resources/hola.tmpl", data)
case gin.MIMEJSON:
c.JSON(200, data)
}
})
}
by default, gin parses the Accept
header.
If you want to change the behaviour, just add a middleware:
r.Use(func(c *gin.Context) {
var format struct {
Format `form:"format"`
}
c.Bind(&format)
switch format.Format {
case "xml":
c.SetNegotiatedFormat(gin.MIMEXML)
case "json" || "":
c.SetNegotiatedFormat(gin.MIMEJSON)
default:
c.Fail(406, "Not Acceptable")
}
})
resource?format=json
from gin.
An update:
c.Negotiate(200, gin.Negotiate{
Offered: []string{gin.MIMEJSON, gin.MIMEXML},
Data: jsonData,
XMLData: xmlData,
})
from gin.
Lines 281 to 338 in 275bdc1
from gin.
@arthurlaveau I did,
I wrote a library to solve the issue. It works with the base go net package.
It works natively with gorilla mux and go-chi.
it could work with gin gonic with a little effort. If you want to contribute feel free
Check the example: https://github.com/Athosone/golib/tree/main/examples/media-type-versioning
from gin.
Interesting!
Anyway, /api/?format=xml/json
, api.json/xml
do not look very standard.
But using the Accept header looks interesting.
Idea, we could add a:
c.Render(code, binding, data)
it should be used like this:
c.Render(200, binding.JSON, data)
and then add a stric-Accept middleware.
from gin.
I'm going out of town for at least a week (vacation :)), so if anyone want to jump in on this, please do.
from gin.
Yeah this popped out at me about gin. I guess I could create a middleware encoder that has the negotiation after the .Next, but it seems like this should be something that is done automatically by gin.
from gin.
I'm interested in this feature as well. It would be nice to have the control to manipulate the response for each format as well.
from gin.
👍 to @alexandernyquist's request for content negotiation
from gin.
I have a proposal for Content Negociation in Gin:
func (c *Context) NegotiatedFormat() string {
if c.negotiatedFormat != "" {
// Evaluate Accept header
c.negotiatedFormat = "application/json" or "application/xml" or "text/html" ...
}
return c.negotiatedFormat
}
This method is lazily initialized, so the performance will not be affected in the current implementation.
It represents the default content negotiation policy but it can be changed with a middleware by calling:
func (c *Context) SetNegotiatedFormat(format string) {
c.negotiatedFormat = format
}
from gin.
How about accepting a file extention in the url like /api/resource.json
and /api/resource.xml
?
from gin.
How about accepting a file extention in the url like /api/resource.json and /api/resource.xml?
Two ideas:
- Using params
func main() {
r := gin.Default()
r.Use(func(c *gin.Context) {
extension := c.Params.ByName("ext")
switch extension {
case "json":
c.SetNegotiatedFormat(gin.MIMEJSON)
case "xml":
c.SetNegotiatedFormat(gin.MIMEJSON)
default:
c.Fail(400, "unknown extension")
}
})
r.GET("/resource.:ext", func(c *gin.Context) {
data := gin.H{"status": "ok"}
switch c.NegotiateFormat(gin.MIMEJSON, gin.MIMEXML) {
case gin.MIMEXML:
c.XML(200, data)
case gin.MIMEJSON:
c.JSON(200, data)
}
})
}
- Using several routes and inspecting the extension:
package main
import "fmt"
import "github.com/gin-gonic/gin"
import "path/filepath"
func main() {
r := gin.Default()
// Create a route group, so this middleware is just applied to this group
negotiation := r.Group("/", func(c *gin.Context) {
switch filepath.Ext(c.Request.URL.Path); {
case "json" || "":
c.SetNegotiatedFormat(gin.MIMEJSON)
case "xml":
c.SetNegotiatedFormat(gin.MIMEJSON)
default:
c.Fail(400, "unknown extension")
}
})
negotiation.GET("/hola.json", resourceHandler)
negotiation.GET("/hola.xml", resourceHandler)
r.Run(":8080")
}
func resourceHandler(c *gin.Context) {
switch c.NegotiateFormat(gin.MIMEJSON, gin.MIMEXML) {
case gin.MIMEXML:
c.XML(200, data)
case gin.MIMEJSON:
c.JSON(200, gin.H{"status": "ok"})
}
}
from gin.
I am also testing a new API:
c.Negotiate(200, gin.H{
"html.file": "resouces/resource.tmpl",
"xml.data": xmlData,
"*.data": jsonData,
})
from gin.
Content.Negotiate()
- Calls c.NegotiateFormat() internally
- Based in the config map, it renders HTML, XML or JSON in a efficient way.
This is extremely flexible, since you can:
- Change the default HTML render, using
engine.HTMLRender = render
- You can change the negotiation algorithm as explained previously using middlewares.
- It doesn't add performance overhead
- Short, imperative and powerful API
from gin.
I think it could be useful to allow extensions to the Negotiate method, because for example the default being an error could not be the best option for everyone, but as it is it is not possible to modify it, without modifying the library code.
from gin.
@jarrodhroberson did you find a way to achieve mediatype versioning?
from gin.
@Athosone about you ? Did you find a way to achieve it ?
from gin.
@Athosone thank you for your answer. I will take a look at it!
from gin.
Related Issues (20)
- [Suggestions] Introducing an AI-powered robot to assist with daily issue and pull request (PR) tasks.
- Need c.JSON() response like behaviour for c.SSEvent() response for supporting first party clients like OpenAI SDK HOT 2
- custom time.Time type can not use bindQuery get a value. HOT 3
- configure server parameters HOT 1
- The binding:"required" tag does not seem to work for struct types HOT 3
- Is there a way to preserve order in JSON on response? HOT 1
- redirect is not success HOT 1
- I want to use shouldmindbodywith first and then use formafile, but it doesn't work right now HOT 3
- Extend the routing tree entry
- Transparent decompression for gzip, deflate, etc? HOT 1
- Custom time.Time type can not use bindQuery get a value 2 HOT 2
- http: response.Write on hijacked connection from github.com/gin-gonic/gin.(*responseWriter).Write (response_writer.go:83) HOT 1
- Suggestion: Move Print Debug to Run() functions? HOT 7
- AbortWithStatusJSON doesn't abort context HOT 2
- I translated `tree.go` into java
- GetUint and GetUint64 wrong return HOT 1
- Multipart form-data request with empty key for single file upload
- gin with golang 1.21, 1.22 DATA RACE
- gin detects `DATA RACE` with Go1.21, Go1.22 HOT 7
- Please consider remove github.com/bytedance/sonic from dependencies HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from gin.