Skip to content
This repository has been archived by the owner on Apr 24, 2018. It is now read-only.

Add support for range of media types in Accept header #19

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 19 additions & 13 deletions routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func (m *RouteMux) AddRoute(method string, pattern string, handler http.HandlerF
if strings.HasPrefix(part, ":") {
expr := "([^/]+)"
//a user may choose to override the defult expression
// similar to expressjs: ‘/user/:id([0-9]+)’
// similar to expressjs: ‘/user/:id([0-9]+)’
if index := strings.Index(part, "("); index != -1 {
expr = part[index:]
part = part[:index]
Expand Down Expand Up @@ -138,13 +138,15 @@ func (m *RouteMux) Filter(filter http.HandlerFunc) {

// FilterParam adds the middleware filter iff the REST URL parameter exists.
func (m *RouteMux) FilterParam(param string, filter http.HandlerFunc) {
if !strings.HasPrefix(param,":") {
param = ":"+param
if !strings.HasPrefix(param, ":") {
param = ":" + param
}

m.Filter(func(w http.ResponseWriter, r *http.Request) {
p := r.URL.Query().Get(param)
if len(p) > 0 { filter(w, r) }
if len(p) > 0 {
filter(w, r)
}
})
}

Expand Down Expand Up @@ -248,6 +250,14 @@ func (w *responseWriter) WriteHeader(code int) {
// code that serializes resources and writes to the
// http response.

type contentSeralizer func(w http.ResponseWriter, v interface{})

var contentHandler = map[string]contentSeralizer{
applicationJson: ServeJson,
applicationXml: ServeXml,
textXml: ServeXml,
}

// ServeJson replies to the request with a JSON
// representation of resource v.
func ServeJson(w http.ResponseWriter, v interface{}) {
Expand Down Expand Up @@ -304,14 +314,10 @@ func ReadXml(r *http.Request, v interface{}) error {
// Accept header.
func ServeFormatted(w http.ResponseWriter, r *http.Request, v interface{}) {
accept := r.Header.Get("Accept")
switch accept {
case applicationJson:
ServeJson(w, v)
case applicationXml, textXml:
ServeXml(w, v)
default:
ServeJson(w, v)
for header, handler := range contentHandler {
if strings.Contains(accept, header) {
handler(w, v)
return
}
}

return
}
67 changes: 67 additions & 0 deletions routes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,51 @@ var FilterId = func(w http.ResponseWriter, r *http.Request) {
}
}

// Test data used for ServeFormatted
type testData struct {
SomeField string
Number int
Test float64
}

// contentTest describes a test for ServerFormatted
type contentTest struct {
Header string
Expected string
}

// The correct formatted data for use by TestContentFormatted
var dataJSON = "{\n \"SomeField\": \"Hi\",\n \"Number\": 20,\n \"Test\": 2.5\n}"
var dataXML = "<testData><SomeField>Hi</SomeField><Number>20</Number><Test>2.5</Test></testData>"

// Various test cases for TestContentFormatted
var contentTester = []contentTest{
contentTest{
"",
dataJSON,
},
contentTest{
"application/json",
dataJSON,
},
contentTest{
"application/xml",
dataXML,
},
contentTest{
"text/xml",
dataXML,
},
contentTest{
"text/plain; q=0.5, text/html,\ntext/xml; q=0.8, text/x-c",
dataXML,
},
contentTest{
"text/html;\ntext/json;",
dataJSON,
},
}

// TestAuthOk tests that an Auth handler will append the
// username and password to to the request URL, and will
// continue processing the request by invoking the handler.
Expand Down Expand Up @@ -148,6 +193,28 @@ func TestFilterParam(t *testing.T) {

}

// TestContentFormatted tests the basic Accept-Header content matching.
func TestContentFormatted(t *testing.T) {
recorder := httptest.NewRecorder()
response, _ := http.NewRequest("GET", "localhost/does/not/matter", nil)

data := testData{
SomeField: "Hi",
Number: 20,
Test: 2.5,
}

for _, test := range contentTester {
response.Header.Set("Accept", test.Header)
ServeFormatted(recorder, response, data)
if recorder.Body.String() != test.Expected {
t.Fatalf("Sent %q header, expected %q. Got %q", test.Header, test.Expected, recorder.Body.String())
}
recorder.Body.Reset()
}

}

// Benchmark_RoutedHandler runs a benchmark against
// the RouteMux using the default settings.
func Benchmark_RoutedHandler(b *testing.B) {
Expand Down