diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..057d2af --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,19 @@ +name: golangci-lint +on: + push: + tags: + - v* + branches: + - master + - main + pull_request: +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: golangci-lint + uses: golangci/golangci-lint-action@v2 + with: + version: v1.29 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..5e3321f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,24 @@ +name: test +on: + push: + tags: + - v* + branches: + - master + - main + pull_request: +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2 + - uses: actions/cache@v2 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + - run: go mod download + - name: Run tests + run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..22d0d82 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +vendor diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a026204..0000000 --- a/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: go - -go: - - 1.7.x - - 1.8.x - - tip - -install: - - go get -u golang.org/x/tools/cmd/goimports - -script: - - go get -d -t ./... - - go test ./... - - > - goimports -d -e ./ | grep '.*' && { echo; echo "Aborting due to non-empty goimports output."; exit 1; } || : diff --git a/README.md b/README.md index 068e0bf..4d37153 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # render +![tests](https://github.com/go-chi/render/actions/workflows/test.yml/badge.svg) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-chi/render)](https://goreportcard.com/report/github.com/go-chi/render) +[![Go Reference](https://pkg.go.dev/badge/github.com/go-chi/render.svg)](https://pkg.go.dev/github.com/go-chi/render) + The `render` package helps manage HTTP request / response payloads. Every well-designed, robust and maintainable Web Service / REST API also needs @@ -21,4 +25,3 @@ request bodies. Please have a look at the [rest](https://github.com/go-chi/chi/b example which uses the latest chi/render sub-pkg. All feedback is welcome, thank you! - diff --git a/decoder.go b/decoder.go index 849c56b..883b2f8 100644 --- a/decoder.go +++ b/decoder.go @@ -7,6 +7,8 @@ import ( "io" "io/ioutil" "net/http" + + "github.com/ajg/form" ) // Decode is a package-level variable set to our default Decoder. We do this @@ -17,6 +19,8 @@ import ( // bytes allowed to be read from the request body. var Decode = DefaultDecoder +// DefaultDecoder detects the correct decoder for use on an HTTP request and +// marshals into a given interface. func DefaultDecoder(r *http.Request, v interface{}) error { var err error @@ -25,7 +29,8 @@ func DefaultDecoder(r *http.Request, v interface{}) error { err = DecodeJSON(r.Body, v) case ContentTypeXML: err = DecodeXML(r.Body, v) - // case ContentTypeForm: // TODO + case ContentTypeForm: + err = DecodeForm(r.Body, v) default: err = errors.New("render: unable to automatically decode the request content type") } @@ -33,12 +38,20 @@ func DefaultDecoder(r *http.Request, v interface{}) error { return err } +// DecodeJSON decodes a given reader into an interface using the json decoder. func DecodeJSON(r io.Reader, v interface{}) error { - defer io.Copy(ioutil.Discard, r) + defer io.Copy(ioutil.Discard, r) //nolint:errcheck return json.NewDecoder(r).Decode(v) } +// DecodeXML decodes a given reader into an interface using the xml decoder. func DecodeXML(r io.Reader, v interface{}) error { - defer io.Copy(ioutil.Discard, r) + defer io.Copy(ioutil.Discard, r) //nolint:errcheck return xml.NewDecoder(r).Decode(v) } + +// DecodeForm decodes a given reader into an interface using the form decoder. +func DecodeForm(r io.Reader, v interface{}) error { + decoder := form.NewDecoder(r) //nolint:errcheck + return decoder.Decode(v) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e47a59c --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/go-chi/render + +go 1.16 + +require github.com/ajg/form v1.5.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..866445b --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= diff --git a/responder.go b/responder.go index 9365350..66d6bbf 100644 --- a/responder.go +++ b/responder.go @@ -66,7 +66,7 @@ func PlainText(w http.ResponseWriter, r *http.Request, v string) { if status, ok := r.Context().Value(StatusCtxKey).(int); ok { w.WriteHeader(status) } - w.Write([]byte(v)) + w.Write([]byte(v)) //nolint:errcheck } // Data writes raw bytes to the response, setting the Content-Type as @@ -76,7 +76,7 @@ func Data(w http.ResponseWriter, r *http.Request, v []byte) { if status, ok := r.Context().Value(StatusCtxKey).(int); ok { w.WriteHeader(status) } - w.Write(v) + w.Write(v) //nolint:errcheck } // HTML writes a string to the response, setting the Content-Type as text/html. @@ -85,7 +85,7 @@ func HTML(w http.ResponseWriter, r *http.Request, v string) { if status, ok := r.Context().Value(StatusCtxKey).(int); ok { w.WriteHeader(status) } - w.Write([]byte(v)) + w.Write([]byte(v)) //nolint:errcheck } // JSON marshals 'v' to JSON, automatically escaping HTML and setting the @@ -103,7 +103,7 @@ func JSON(w http.ResponseWriter, r *http.Request, v interface{}) { if status, ok := r.Context().Value(StatusCtxKey).(int); ok { w.WriteHeader(status) } - w.Write(buf.Bytes()) + w.Write(buf.Bytes()) //nolint:errcheck } // XML marshals 'v' to JSON, setting the Content-Type as application/xml. It @@ -128,15 +128,15 @@ func XML(w http.ResponseWriter, r *http.Request, v interface{}) { } if !bytes.Contains(b[:findHeaderUntil], []byte("