feature: add XML to context. (#2003)

*  feature: add XML to context.

* Update app.go
pull/2017/head
M. Efe Çetin 2022-08-16 09:05:35 +03:00 committed by GitHub
parent 6669ec4486
commit 95abdacba0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 147 additions and 6 deletions

11
app.go
View File

@ -23,6 +23,7 @@ import (
"time"
"encoding/json"
"encoding/xml"
"github.com/gofiber/fiber/v2/utils"
"github.com/valyala/fasthttp"
@ -324,6 +325,13 @@ type Config struct {
// Default: json.Unmarshal
JSONDecoder utils.JSONUnmarshal `json:"-"`
// XMLEncoder set by an external client of Fiber it will use the provided implementation of a
// XMLMarshal
//
// Allowing for flexibility in using another XML library for encoding
// Default: xml.Marshal
XMLEncoder utils.XMLMarshal `json:"-"`
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only)
// WARNING: When prefork is set to true, only "tcp4" and "tcp6" can be chose.
//
@ -513,6 +521,9 @@ func New(config ...Config) *App {
if app.config.JSONDecoder == nil {
app.config.JSONDecoder = json.Unmarshal
}
if app.config.XMLEncoder == nil {
app.config.XMLEncoder = xml.Marshal
}
if app.config.Network == "" {
app.config.Network = NetworkTCP4
}

19
ctx.go
View File

@ -518,12 +518,7 @@ func (c *Ctx) Format(body interface{}) error {
case "txt":
return c.SendString(b)
case "xml":
raw, err := xml.Marshal(body)
if err != nil {
return fmt.Errorf("error serializing xml: %v", body)
}
c.fasthttp.Response.SetBody(raw)
return nil
return c.XML(body)
}
return c.SendString(b)
}
@ -736,6 +731,18 @@ func (c *Ctx) JSONP(data interface{}, callback ...string) error {
return c.SendString(result)
}
// XML converts any interface or string to XML.
// This method also sets the content header to application/xml.
func (c *Ctx) XML(data interface{}) error {
raw, err := c.app.config.XMLEncoder(data)
if err != nil {
return err
}
c.fasthttp.Response.SetBodyRaw(raw)
c.fasthttp.Response.Header.SetContentType(MIMEApplicationXML)
return nil
}
// Links joins the links followed by the property to populate the response's Link HTTP header field.
func (c *Ctx) Links(link ...string) {
if len(link) == 0 {

View File

@ -12,6 +12,7 @@ import (
"bytes"
"compress/gzip"
"context"
"encoding/xml"
"errors"
"fmt"
"io"
@ -2134,6 +2135,65 @@ func Benchmark_Ctx_JSONP(b *testing.B) {
utils.AssertEqual(b, `emit({"Name":"Grame","Age":20});`, string(c.Response().Body()))
}
// go test -run Test_Ctx_XML
func Test_Ctx_XML(t *testing.T) {
t.Parallel()
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
utils.AssertEqual(t, true, c.JSON(complex(1, 1)) != nil)
type xmlResult struct {
XMLName xml.Name `xml:"Users"`
Names []string `xml:"Names"`
Ages []int `xml:"Ages"`
}
c.XML(xmlResult{
Names: []string{"Grame", "John"},
Ages: []int{1, 12, 20},
})
utils.AssertEqual(t, `<Users><Names>Grame</Names><Names>John</Names><Ages>1</Ages><Ages>12</Ages><Ages>20</Ages></Users>`, string(c.Response().Body()))
utils.AssertEqual(t, "application/xml", string(c.Response().Header.Peek("content-type")))
testEmpty := func(v interface{}, r string) {
err := c.XML(v)
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, r, string(c.Response().Body()))
}
testEmpty(nil, "")
testEmpty("", `<string></string>`)
testEmpty(0, "<int>0</int>")
testEmpty([]int{}, "")
}
// go test -run=^$ -bench=Benchmark_Ctx_XML -benchmem -count=4
func Benchmark_Ctx_XML(b *testing.B) {
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
type SomeStruct struct {
Name string `xml:"Name"`
Age uint8 `xml:"Age"`
}
data := SomeStruct{
Name: "Grame",
Age: 20,
}
var err error
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.XML(data)
}
utils.AssertEqual(b, nil, err)
utils.AssertEqual(b, `<SomeStruct><Name>Grame</Name><Age>20</Age></SomeStruct>`, string(c.Response().Body()))
}
// go test -run Test_Ctx_Links
func Test_Ctx_Links(t *testing.T) {
t.Parallel()

4
utils/xml.go Normal file
View File

@ -0,0 +1,4 @@
package utils
// XMLMarshal returns the XML encoding of v.
type XMLMarshal func(v interface{}) ([]byte, error)

59
utils/xml_test.go Normal file
View File

@ -0,0 +1,59 @@
package utils
import (
"encoding/xml"
"testing"
)
type serversXMLStructure struct {
XMLName xml.Name `xml:"servers"`
Version string `xml:"version,attr"`
Servers []serverXMLStructure `xml:"server"`
}
type serverXMLStructure struct {
XMLName xml.Name `xml:"server"`
Name string `xml:"name"`
}
var xmlString = `<servers version="1"><server><name>fiber one</name></server><server><name>fiber two</name></server></servers>`
func Test_GolangXMLEncoder(t *testing.T) {
t.Parallel()
var (
ss = &serversXMLStructure{
Version: "1",
Servers: []serverXMLStructure{
{Name: "fiber one"},
{Name: "fiber two"},
},
}
xmlEncoder XMLMarshal = xml.Marshal
)
raw, err := xmlEncoder(ss)
AssertEqual(t, err, nil)
AssertEqual(t, string(raw), xmlString)
}
func Test_DefaultXMLEncoder(t *testing.T) {
t.Parallel()
var (
ss = &serversXMLStructure{
Version: "1",
Servers: []serverXMLStructure{
{Name: "fiber one"},
{Name: "fiber two"},
},
}
xmlEncoder XMLMarshal = xml.Marshal
)
raw, err := xmlEncoder(ss)
AssertEqual(t, err, nil)
AssertEqual(t, string(raw), xmlString)
}