mirror of
https://github.com/kataras/iris.git
synced 2026-01-09 13:05:56 +00:00
Version 3.0.0-beta cleaned
This commit is contained in:
317
render/rest/engine.go
Normal file
317
render/rest/engine.go
Normal file
@@ -0,0 +1,317 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
|
||||
"github.com/klauspost/compress/gzip"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// Engine is the generic interface for all responses.
|
||||
type Engine interface {
|
||||
Render(*fasthttp.RequestCtx, interface{}) error
|
||||
//used only if config gzip is enabled
|
||||
RenderGzip(*fasthttp.RequestCtx, interface{}) error
|
||||
}
|
||||
|
||||
// Head defines the basic ContentType and Status fields.
|
||||
type Head struct {
|
||||
ContentType string
|
||||
Status int
|
||||
}
|
||||
|
||||
// Data built-in renderer.
|
||||
type Data struct {
|
||||
Head
|
||||
}
|
||||
|
||||
// JSON built-in renderer.
|
||||
type JSON struct {
|
||||
Head
|
||||
Indent bool
|
||||
UnEscapeHTML bool
|
||||
Prefix []byte
|
||||
StreamingJSON bool
|
||||
}
|
||||
|
||||
// JSONP built-in renderer.
|
||||
type JSONP struct {
|
||||
Head
|
||||
Indent bool
|
||||
Callback string
|
||||
}
|
||||
|
||||
// Text built-in renderer.
|
||||
type Text struct {
|
||||
Head
|
||||
}
|
||||
|
||||
// XML built-in renderer.
|
||||
type XML struct {
|
||||
Head
|
||||
Indent bool
|
||||
Prefix []byte
|
||||
}
|
||||
|
||||
// Write outputs the header content.
|
||||
func (h Head) Write(ctx *fasthttp.RequestCtx) {
|
||||
ctx.Response.Header.Set(ContentType, h.ContentType)
|
||||
ctx.SetStatusCode(h.Status)
|
||||
}
|
||||
|
||||
// Render a data response.
|
||||
func (d Data) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
c := string(ctx.Request.Header.Peek(ContentType))
|
||||
w := ctx.Response.BodyWriter()
|
||||
if c != "" {
|
||||
d.Head.ContentType = c
|
||||
}
|
||||
|
||||
d.Head.Write(ctx)
|
||||
w.Write(v.([]byte))
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// RenderGzip a data response using gzip compression.
|
||||
func (d Data) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
c := string(ctx.Request.Header.Peek(ContentType))
|
||||
if c != "" {
|
||||
d.Head.ContentType = c
|
||||
}
|
||||
|
||||
d.Head.Write(ctx)
|
||||
_, err := fasthttp.WriteGzip(ctx.Response.BodyWriter(), v.([]byte))
|
||||
if err == nil {
|
||||
ctx.Response.Header.Add("Content-Encoding", "gzip")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Render a JSON response.
|
||||
func (j JSON) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
if j.StreamingJSON {
|
||||
return j.renderStreamingJSON(ctx, v)
|
||||
}
|
||||
|
||||
var result []byte
|
||||
var err error
|
||||
|
||||
if j.Indent {
|
||||
result, err = json.MarshalIndent(v, "", " ")
|
||||
result = append(result, '\n')
|
||||
} else {
|
||||
result, err = json.Marshal(v)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Unescape HTML if needed.
|
||||
if j.UnEscapeHTML {
|
||||
result = bytes.Replace(result, []byte("\\u003c"), []byte("<"), -1)
|
||||
result = bytes.Replace(result, []byte("\\u003e"), []byte(">"), -1)
|
||||
result = bytes.Replace(result, []byte("\\u0026"), []byte("&"), -1)
|
||||
}
|
||||
w := ctx.Response.BodyWriter()
|
||||
// JSON marshaled fine, write out the result.
|
||||
j.Head.Write(ctx)
|
||||
if len(j.Prefix) > 0 {
|
||||
w.Write(j.Prefix)
|
||||
}
|
||||
w.Write(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RenderGzip a JSON response using gzip compression.
|
||||
func (j JSON) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
if j.StreamingJSON {
|
||||
return j.renderStreamingJSONGzip(ctx, v)
|
||||
}
|
||||
|
||||
var result []byte
|
||||
var err error
|
||||
|
||||
if j.Indent {
|
||||
result, err = json.MarshalIndent(v, "", " ")
|
||||
result = append(result, '\n')
|
||||
} else {
|
||||
result, err = json.Marshal(v)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Response.Header.Add("Content-Encoding", "gzip")
|
||||
|
||||
// Unescape HTML if needed.
|
||||
if j.UnEscapeHTML {
|
||||
result = bytes.Replace(result, []byte("\\u003c"), []byte("<"), -1)
|
||||
result = bytes.Replace(result, []byte("\\u003e"), []byte(">"), -1)
|
||||
result = bytes.Replace(result, []byte("\\u0026"), []byte("&"), -1)
|
||||
}
|
||||
w := gzip.NewWriter(ctx.Response.BodyWriter())
|
||||
// JSON marshaled fine, write out the result.
|
||||
j.Head.Write(ctx)
|
||||
if len(j.Prefix) > 0 {
|
||||
w.Write(j.Prefix)
|
||||
}
|
||||
w.Write(result)
|
||||
w.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j JSON) renderStreamingJSON(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
j.Head.Write(ctx)
|
||||
w := ctx.Response.BodyWriter()
|
||||
if len(j.Prefix) > 0 {
|
||||
w.Write(j.Prefix)
|
||||
}
|
||||
return json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
|
||||
func (j JSON) renderStreamingJSONGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
ctx.Response.Header.Add("Content-Encoding", "gzip")
|
||||
j.Head.Write(ctx)
|
||||
w := gzip.NewWriter(ctx.Response.BodyWriter())
|
||||
if len(j.Prefix) > 0 {
|
||||
w.Write(j.Prefix)
|
||||
}
|
||||
w.Close()
|
||||
return json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
|
||||
// Render a JSONP response.
|
||||
func (j JSONP) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
var result []byte
|
||||
var err error
|
||||
|
||||
if j.Indent {
|
||||
result, err = json.MarshalIndent(v, "", " ")
|
||||
} else {
|
||||
result, err = json.Marshal(v)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w := ctx.Response.BodyWriter()
|
||||
|
||||
// JSON marshaled fine, write out the result.
|
||||
j.Head.Write(ctx)
|
||||
w.Write([]byte(j.Callback + "("))
|
||||
w.Write(result)
|
||||
w.Write([]byte(");"))
|
||||
|
||||
// If indenting, append a new line.
|
||||
if j.Indent {
|
||||
w.Write([]byte("\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RenderGzip a JSONP response using gzip compression.
|
||||
func (j JSONP) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
var result []byte
|
||||
var err error
|
||||
|
||||
if j.Indent {
|
||||
result, err = json.MarshalIndent(v, "", " ")
|
||||
} else {
|
||||
result, err = json.Marshal(v)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w := gzip.NewWriter(ctx.Response.BodyWriter())
|
||||
|
||||
ctx.Response.Header.Add("Content-Encoding", "gzip")
|
||||
// JSON marshaled fine, write out the result.
|
||||
j.Head.Write(ctx)
|
||||
w.Write([]byte(j.Callback + "("))
|
||||
w.Write(result)
|
||||
w.Write([]byte(");"))
|
||||
|
||||
// If indenting, append a new line.
|
||||
if j.Indent {
|
||||
w.Write([]byte("\n"))
|
||||
}
|
||||
w.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Render a text response.
|
||||
func (t Text) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
c := string(ctx.Request.Header.Peek(ContentType))
|
||||
if c != "" {
|
||||
t.Head.ContentType = c
|
||||
}
|
||||
w := ctx.Response.BodyWriter()
|
||||
t.Head.Write(ctx)
|
||||
w.Write([]byte(v.(string)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// RenderGzip a Text response using gzip compression.
|
||||
func (t Text) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
c := string(ctx.Request.Header.Peek(ContentType))
|
||||
if c != "" {
|
||||
t.Head.ContentType = c
|
||||
}
|
||||
ctx.Response.Header.Add("Content-Encoding", "gzip")
|
||||
t.Head.Write(ctx)
|
||||
fasthttp.WriteGzip(ctx.Response.BodyWriter(), []byte(v.(string)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Render an XML response.
|
||||
func (x XML) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
var result []byte
|
||||
var err error
|
||||
|
||||
if x.Indent {
|
||||
result, err = xml.MarshalIndent(v, "", " ")
|
||||
result = append(result, '\n')
|
||||
} else {
|
||||
result, err = xml.Marshal(v)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// XML marshaled fine, write out the result.
|
||||
x.Head.Write(ctx)
|
||||
w := ctx.Response.BodyWriter()
|
||||
if len(x.Prefix) > 0 {
|
||||
w.Write(x.Prefix)
|
||||
}
|
||||
w.Write(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RenderGzip an XML response using gzip compression.
|
||||
func (x XML) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
|
||||
var result []byte
|
||||
var err error
|
||||
|
||||
if x.Indent {
|
||||
result, err = xml.MarshalIndent(v, "", " ")
|
||||
result = append(result, '\n')
|
||||
} else {
|
||||
result, err = xml.Marshal(v)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Response.Header.Add("Content-Encoding", "gzip")
|
||||
// XML marshaled fine, write out the result.
|
||||
x.Head.Write(ctx)
|
||||
w := gzip.NewWriter(ctx.Response.BodyWriter())
|
||||
if len(x.Prefix) > 0 {
|
||||
w.Write(x.Prefix)
|
||||
}
|
||||
w.Write(result)
|
||||
w.Close()
|
||||
return nil
|
||||
}
|
||||
172
render/rest/render.go
Normal file
172
render/rest/render.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/utils"
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
"github.com/russross/blackfriday"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
const (
|
||||
// ContentBinary header value for binary data.
|
||||
ContentBinary = "application/octet-stream"
|
||||
// ContentJSON header value for JSON data.
|
||||
ContentJSON = "application/json"
|
||||
// ContentJSONP header value for JSONP data.
|
||||
ContentJSONP = "application/javascript"
|
||||
// ContentLength header constant.
|
||||
ContentLength = "Content-Length"
|
||||
// ContentText header value for Text data.
|
||||
ContentText = "text/plain"
|
||||
// ContentType header constant.
|
||||
ContentType = "Content-Type"
|
||||
// ContentXML header value for XML data.
|
||||
ContentXML = "text/xml"
|
||||
)
|
||||
|
||||
// bufPool represents a reusable buffer pool for executing templates into.
|
||||
var bufPool *utils.BufferPool
|
||||
|
||||
// Render is a service that provides functions for easily writing JSON, XML,
|
||||
// binary data, and HTML templates out to a HTTP Response.
|
||||
type Render struct {
|
||||
// Customize Secure with an Options struct.
|
||||
Config config.Rest
|
||||
CompiledCharset string
|
||||
}
|
||||
|
||||
// New constructs a new Render instance with the supplied configs.
|
||||
func New(cfg ...config.Rest) *Render {
|
||||
if bufPool == nil {
|
||||
bufPool = utils.NewBufferPool(64)
|
||||
}
|
||||
|
||||
c := config.DefaultRest().Merge(cfg)
|
||||
|
||||
r := &Render{
|
||||
Config: c,
|
||||
}
|
||||
|
||||
r.prepareConfig()
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Render) prepareConfig() {
|
||||
// Fill in the defaults if need be.
|
||||
if len(r.Config.Charset) == 0 {
|
||||
r.Config.Charset = config.Charset
|
||||
}
|
||||
r.CompiledCharset = "; charset=" + r.Config.Charset
|
||||
}
|
||||
|
||||
// Render is the generic function called by XML, JSON, Data, HTML, and can be called by custom implementations.
|
||||
func (r *Render) Render(ctx *fasthttp.RequestCtx, e Engine, data interface{}) error {
|
||||
var err error
|
||||
if r.Config.Gzip {
|
||||
err = e.RenderGzip(ctx, data)
|
||||
} else {
|
||||
err = e.Render(ctx, data)
|
||||
}
|
||||
|
||||
if err != nil && !r.Config.DisableHTTPErrorRendering {
|
||||
ctx.Response.SetBodyString(err.Error())
|
||||
ctx.Response.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Data writes out the raw bytes as binary data.
|
||||
func (r *Render) Data(ctx *fasthttp.RequestCtx, status int, v []byte) error {
|
||||
head := Head{
|
||||
ContentType: ContentBinary,
|
||||
Status: status,
|
||||
}
|
||||
|
||||
d := Data{
|
||||
Head: head,
|
||||
}
|
||||
|
||||
return r.Render(ctx, d, v)
|
||||
}
|
||||
|
||||
// JSON marshals the given interface object and writes the JSON response.
|
||||
func (r *Render) JSON(ctx *fasthttp.RequestCtx, status int, v interface{}) error {
|
||||
head := Head{
|
||||
ContentType: ContentJSON + r.CompiledCharset,
|
||||
Status: status,
|
||||
}
|
||||
|
||||
j := JSON{
|
||||
Head: head,
|
||||
Indent: r.Config.IndentJSON,
|
||||
Prefix: r.Config.PrefixJSON,
|
||||
UnEscapeHTML: r.Config.UnEscapeHTML,
|
||||
StreamingJSON: r.Config.StreamingJSON,
|
||||
}
|
||||
|
||||
return r.Render(ctx, j, v)
|
||||
}
|
||||
|
||||
// JSONP marshals the given interface object and writes the JSON response.
|
||||
func (r *Render) JSONP(ctx *fasthttp.RequestCtx, status int, callback string, v interface{}) error {
|
||||
head := Head{
|
||||
ContentType: ContentJSONP + r.CompiledCharset,
|
||||
Status: status,
|
||||
}
|
||||
|
||||
j := JSONP{
|
||||
Head: head,
|
||||
Indent: r.Config.IndentJSON,
|
||||
Callback: callback,
|
||||
}
|
||||
|
||||
return r.Render(ctx, j, v)
|
||||
}
|
||||
|
||||
// Text writes out a string as plain text.
|
||||
func (r *Render) Text(ctx *fasthttp.RequestCtx, status int, v string) error {
|
||||
head := Head{
|
||||
ContentType: ContentText + r.CompiledCharset,
|
||||
Status: status,
|
||||
}
|
||||
|
||||
t := Text{
|
||||
Head: head,
|
||||
}
|
||||
|
||||
return r.Render(ctx, t, v)
|
||||
}
|
||||
|
||||
// XML marshals the given interface object and writes the XML response.
|
||||
func (r *Render) XML(ctx *fasthttp.RequestCtx, status int, v interface{}) error {
|
||||
head := Head{
|
||||
ContentType: ContentXML + r.CompiledCharset,
|
||||
Status: status,
|
||||
}
|
||||
|
||||
x := XML{
|
||||
Head: head,
|
||||
Indent: r.Config.IndentXML,
|
||||
Prefix: r.Config.PrefixXML,
|
||||
}
|
||||
|
||||
return r.Render(ctx, x, v)
|
||||
}
|
||||
|
||||
// Markdown parses and returns the converted html from a markdown []byte
|
||||
// accepts two parameters
|
||||
// first is the http status code
|
||||
// second is the markdown string
|
||||
//
|
||||
// Note that: Works different than the other rest's functions.
|
||||
func (r *Render) Markdown(markdownBytes []byte) string {
|
||||
buf := blackfriday.MarkdownCommon(markdownBytes)
|
||||
if r.Config.MarkdownSanitize {
|
||||
buf = bluemonday.UGCPolicy().SanitizeBytes(buf)
|
||||
}
|
||||
|
||||
return string(buf)
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user