Skip to content

Commit 2889250

Browse files
joeybloggsjoeybloggs
authored andcommitted
Add JSON, JSONBytes, JSONP, XML, XMLBytes, Text, TextBytes context helper functions.
1 parent a4b066e commit 2889250

File tree

2 files changed

+206
-0
lines changed

2 files changed

+206
-0
lines changed

context.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package lars
22

33
import (
4+
"encoding/json"
5+
"encoding/xml"
46
"io"
57
"net"
68
"net/http"
@@ -42,6 +44,13 @@ type Context interface {
4244
AcceptedLanguages(lowercase bool) []string
4345
HandlerName() string
4446
Stream(step func(w io.Writer) bool)
47+
JSON(int, interface{}) error
48+
JSONBytes(int, []byte) error
49+
JSONP(int, interface{}, string) error
50+
XML(int, interface{}) error
51+
XMLBytes(int, []byte) error
52+
Text(int, string) error
53+
TextBytes(int, []byte) error
4554
Attachment(r io.Reader, filename string) (err error)
4655
Inline(r io.Reader, filename string) (err error)
4756
BaseContext() *Ctx
@@ -216,6 +225,89 @@ func (c *Ctx) Next() {
216225
c.handlers[c.index](c.parent)
217226
}
218227

228+
// http response helpers
229+
230+
// JSON marshals provided interface + returns JSON + status code
231+
func (c *Ctx) JSON(code int, i interface{}) (err error) {
232+
233+
b, err := json.Marshal(i)
234+
if err != nil {
235+
return err
236+
}
237+
238+
return c.JSONBytes(code, b)
239+
}
240+
241+
// JSONBytes returns provided JSON response with status code
242+
func (c *Ctx) JSONBytes(code int, b []byte) (err error) {
243+
244+
c.response.Header().Set(ContentType, ApplicationJSONCharsetUTF8)
245+
c.response.WriteHeader(code)
246+
_, err = c.response.Write(b)
247+
return
248+
}
249+
250+
// JSONP sends a JSONP response with status code and uses `callback` to construct
251+
// the JSONP payload.
252+
func (c *Ctx) JSONP(code int, i interface{}, callback string) (err error) {
253+
254+
b, e := json.Marshal(i)
255+
if e != nil {
256+
err = e
257+
return
258+
}
259+
260+
c.response.Header().Set(ContentType, ApplicationJavaScriptCharsetUTF8)
261+
c.response.WriteHeader(code)
262+
263+
if _, err = c.response.Write([]byte(callback + "(")); err == nil {
264+
265+
if _, err = c.response.Write(b); err == nil {
266+
_, err = c.response.Write([]byte(");"))
267+
}
268+
}
269+
270+
return
271+
}
272+
273+
// XML marshals provided interface + returns XML + status code
274+
func (c *Ctx) XML(code int, i interface{}) error {
275+
276+
b, err := xml.Marshal(i)
277+
if err != nil {
278+
return err
279+
}
280+
281+
return c.XMLBytes(code, b)
282+
}
283+
284+
// XMLBytes returns provided XML response with status code
285+
func (c *Ctx) XMLBytes(code int, b []byte) (err error) {
286+
287+
c.response.Header().Set(ContentType, ApplicationXMLCharsetUTF8)
288+
c.response.WriteHeader(code)
289+
290+
if _, err = c.response.Write([]byte(xml.Header)); err == nil {
291+
_, err = c.response.Write(b)
292+
}
293+
294+
return
295+
}
296+
297+
// Text returns the provided string with status code
298+
func (c *Ctx) Text(code int, s string) error {
299+
return c.TextBytes(code, []byte(s))
300+
}
301+
302+
// TextBytes returns the provided response with status code
303+
func (c *Ctx) TextBytes(code int, b []byte) (err error) {
304+
305+
c.response.Header().Set(ContentType, TextPlainCharsetUTF8)
306+
c.response.WriteHeader(code)
307+
_, err = c.response.Write(b)
308+
return
309+
}
310+
219311
// http request helpers
220312

221313
// ClientIP implements a best effort algorithm to return the real client IP, it parses

context_test.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package lars
22

33
import (
4+
"encoding/xml"
45
"io"
56
"net/http"
67
"net/http/httptest"
@@ -501,3 +502,116 @@ func TestAcceptedLanguages(t *testing.T) {
501502

502503
Equal(t, languages, []string{})
503504
}
505+
506+
type zombie struct {
507+
ID int `json:"id" xml:"id"`
508+
Name string `json:"name" xml:"name"`
509+
}
510+
511+
func TestXML(t *testing.T) {
512+
xmlData := `<zombie><id>1</id><name>Patient Zero</name></zombie>`
513+
514+
l := New()
515+
l.Get("/xml", func(c Context) {
516+
c.XML(http.StatusOK, zombie{1, "Patient Zero"})
517+
})
518+
l.Get("/badxml", func(c Context) {
519+
if err := c.XML(http.StatusOK, func() {}); err != nil {
520+
http.Error(c.Response(), err.Error(), http.StatusInternalServerError)
521+
}
522+
})
523+
524+
hf := l.Serve()
525+
526+
r, _ := http.NewRequest(GET, "/xml", nil)
527+
w := httptest.NewRecorder()
528+
hf.ServeHTTP(w, r)
529+
530+
Equal(t, w.Code, http.StatusOK)
531+
Equal(t, w.Header().Get(ContentType), ApplicationXMLCharsetUTF8)
532+
Equal(t, w.Body.String(), xml.Header+xmlData)
533+
534+
r, _ = http.NewRequest(GET, "/badxml", nil)
535+
w = httptest.NewRecorder()
536+
hf.ServeHTTP(w, r)
537+
538+
Equal(t, w.Code, http.StatusInternalServerError)
539+
Equal(t, w.Header().Get(ContentType), TextPlainCharsetUTF8)
540+
Equal(t, w.Body.String(), "xml: unsupported type: func()\n")
541+
}
542+
543+
func TestJSON(t *testing.T) {
544+
jsonData := `{"id":1,"name":"Patient Zero"}`
545+
callbackFunc := "CallbackFunc"
546+
547+
l := New()
548+
l.Get("/json", func(c Context) {
549+
c.JSON(http.StatusOK, zombie{1, "Patient Zero"})
550+
})
551+
l.Get("/badjson", func(c Context) {
552+
if err := c.JSON(http.StatusOK, func() {}); err != nil {
553+
http.Error(c.Response(), err.Error(), http.StatusInternalServerError)
554+
}
555+
})
556+
l.Get("/jsonp", func(c Context) {
557+
c.JSONP(http.StatusOK, zombie{1, "Patient Zero"}, callbackFunc)
558+
})
559+
l.Get("/badjsonp", func(c Context) {
560+
if err := c.JSONP(http.StatusOK, func() {}, callbackFunc); err != nil {
561+
http.Error(c.Response(), err.Error(), http.StatusInternalServerError)
562+
}
563+
})
564+
565+
hf := l.Serve()
566+
567+
r, _ := http.NewRequest(GET, "/json", nil)
568+
w := httptest.NewRecorder()
569+
hf.ServeHTTP(w, r)
570+
571+
Equal(t, w.Code, http.StatusOK)
572+
Equal(t, w.Header().Get(ContentType), ApplicationJSONCharsetUTF8)
573+
Equal(t, w.Body.String(), jsonData)
574+
575+
r, _ = http.NewRequest(GET, "/badjson", nil)
576+
w = httptest.NewRecorder()
577+
hf.ServeHTTP(w, r)
578+
579+
Equal(t, w.Code, http.StatusInternalServerError)
580+
Equal(t, w.Header().Get(ContentType), TextPlainCharsetUTF8)
581+
Equal(t, w.Body.String(), "json: unsupported type: func()\n")
582+
583+
r, _ = http.NewRequest(GET, "/jsonp", nil)
584+
w = httptest.NewRecorder()
585+
hf.ServeHTTP(w, r)
586+
587+
Equal(t, w.Code, http.StatusOK)
588+
Equal(t, w.Header().Get(ContentType), ApplicationJavaScriptCharsetUTF8)
589+
Equal(t, w.Body.String(), callbackFunc+"("+jsonData+");")
590+
591+
r, _ = http.NewRequest(GET, "/badjsonp", nil)
592+
w = httptest.NewRecorder()
593+
hf.ServeHTTP(w, r)
594+
595+
Equal(t, w.Code, http.StatusInternalServerError)
596+
Equal(t, w.Header().Get(ContentType), TextPlainCharsetUTF8)
597+
Equal(t, w.Body.String(), "json: unsupported type: func()\n")
598+
}
599+
600+
func TestText(t *testing.T) {
601+
txtData := `OMG I'm infected! #zombie`
602+
603+
l := New()
604+
l.Get("/text", func(c Context) {
605+
c.Text(http.StatusOK, txtData)
606+
})
607+
608+
hf := l.Serve()
609+
610+
r, _ := http.NewRequest(GET, "/text", nil)
611+
w := httptest.NewRecorder()
612+
hf.ServeHTTP(w, r)
613+
614+
Equal(t, w.Code, http.StatusOK)
615+
Equal(t, w.Header().Get(ContentType), TextPlainCharsetUTF8)
616+
Equal(t, w.Body.String(), txtData)
617+
}

0 commit comments

Comments
 (0)