diff --git a/binder.go b/bind.go similarity index 98% rename from binder.go rename to bind.go index dd24eff5..8b169250 100644 --- a/binder.go +++ b/bind.go @@ -22,7 +22,7 @@ type ( // BindUnmarshaler is the interface used to wrap the UnmarshalParam method. BindUnmarshaler interface { - // UnmarshalParam decodes and assigns a value from an HTML form. + // UnmarshalParam decodes and assigns a value from an form or query param. UnmarshalParam(src string) error } ) diff --git a/binder_test.go b/bind_test.go similarity index 78% rename from binder_test.go rename to bind_test.go index 6f495fc6..fdf97f91 100644 --- a/binder_test.go +++ b/bind_test.go @@ -15,7 +15,7 @@ import ( ) type ( - binderTestStruct struct { + bindTestStruct struct { I int I8 int8 I16 int16 @@ -36,19 +36,16 @@ type ( Tptr *Timestamp } - // Timestamp is a simple datatype which implements the Scanner interface - // used for testing. Timestamp time.Time ) -// UnmarshalParam unmarshals a value from an HTML form into a Timestamp value func (t *Timestamp) UnmarshalParam(src string) error { ts, err := time.Parse(time.RFC3339, src) *t = Timestamp(ts) return err } -func (t binderTestStruct) GetCantSet() string { +func (t bindTestStruct) GetCantSet() string { return t.cantSet } @@ -72,19 +69,19 @@ var values = map[string][]string{ "Tptr": {"2016-12-06T19:09:05+01:00"}, } -func TestBinderJSON(t *testing.T) { - testBinderOkay(t, strings.NewReader(userJSON), MIMEApplicationJSON) - testBinderError(t, strings.NewReader(invalidContent), MIMEApplicationJSON) +func TestBindJSON(t *testing.T) { + testBindOkay(t, strings.NewReader(userJSON), MIMEApplicationJSON) + testBindError(t, strings.NewReader(invalidContent), MIMEApplicationJSON) } -func TestBinderXML(t *testing.T) { - testBinderOkay(t, strings.NewReader(userXML), MIMEApplicationXML) - testBinderError(t, strings.NewReader(invalidContent), MIMEApplicationXML) +func TestBindXML(t *testing.T) { + testBindOkay(t, strings.NewReader(userXML), MIMEApplicationXML) + testBindError(t, strings.NewReader(invalidContent), MIMEApplicationXML) } -func TestBinderForm(t *testing.T) { - testBinderOkay(t, strings.NewReader(userForm), MIMEApplicationForm) - testBinderError(t, nil, MIMEApplicationForm) +func TestBindForm(t *testing.T) { + testBindOkay(t, strings.NewReader(userForm), MIMEApplicationForm) + testBindError(t, nil, MIMEApplicationForm) e := New() req, _ := http.NewRequest(POST, "/", strings.NewReader(userForm)) rec := httptest.NewRecorder() @@ -95,7 +92,7 @@ func TestBinderForm(t *testing.T) { assert.Error(t, err) } -func TestBinderQueryParams(t *testing.T) { +func TestBindQueryParams(t *testing.T) { e := New() req, _ := http.NewRequest(GET, "/?id=1&name=Jon Snow", nil) rec := httptest.NewRecorder() @@ -108,14 +105,14 @@ func TestBinderQueryParams(t *testing.T) { } } -func TestBinderScanner(t *testing.T) { +func TestBindUnmarshalParam(t *testing.T) { e := New() req, _ := http.NewRequest(GET, "/?ts=2016-12-06T19:09:05Z", nil) rec := httptest.NewRecorder() c := e.NewContext(req, rec) - var result struct { + result := struct { T Timestamp `query:"ts"` - } + }{} err := c.Bind(&result) if assert.NoError(t, err) { // assert.Equal(t, Timestamp(reflect.TypeOf(&Timestamp{}), time.Date(2016, 12, 6, 19, 9, 5, 0, time.UTC)), result.T) @@ -123,42 +120,42 @@ func TestBinderScanner(t *testing.T) { } } -func TestBinderScannerPtr(t *testing.T) { +func TestBindUnmarshalParamPtr(t *testing.T) { e := New() req, _ := http.NewRequest(GET, "/?ts=2016-12-06T19:09:05Z", nil) rec := httptest.NewRecorder() c := e.NewContext(req, rec) - var result struct { + result := struct { Tptr *Timestamp `query:"ts"` - } + }{} err := c.Bind(&result) if assert.NoError(t, err) { assert.Equal(t, Timestamp(time.Date(2016, 12, 6, 19, 9, 5, 0, time.UTC)), *result.Tptr) } } -func TestBinderMultipartForm(t *testing.T) { +func TestBindMultipartForm(t *testing.T) { body := new(bytes.Buffer) mw := multipart.NewWriter(body) mw.WriteField("id", "1") mw.WriteField("name", "Jon Snow") mw.Close() - testBinderOkay(t, body, mw.FormDataContentType()) + testBindOkay(t, body, mw.FormDataContentType()) } -func TestBinderUnsupportedMediaType(t *testing.T) { - testBinderError(t, strings.NewReader(invalidContent), MIMEApplicationJSON) +func TestBindUnsupportedMediaType(t *testing.T) { + testBindError(t, strings.NewReader(invalidContent), MIMEApplicationJSON) } -func TestBinderbindData(t *testing.T) { - ts := new(binderTestStruct) +func TestBindbindData(t *testing.T) { + ts := new(bindTestStruct) b := new(DefaultBinder) b.bindData(ts, values, "form") - assertBinderTestStruct(t, ts) + assertBindTestStruct(t, ts) } -func TestBinderSetWithProperType(t *testing.T) { - ts := new(binderTestStruct) +func TestBindSetWithProperType(t *testing.T) { + ts := new(bindTestStruct) typ := reflect.TypeOf(ts).Elem() val := reflect.ValueOf(ts).Elem() for i := 0; i < typ.NumField(); i++ { @@ -174,7 +171,7 @@ func TestBinderSetWithProperType(t *testing.T) { err := setWithProperType(typeField.Type.Kind(), val, structField) assert.NoError(t, err) } - assertBinderTestStruct(t, ts) + assertBindTestStruct(t, ts) type foo struct { Bar bytes.Buffer @@ -185,8 +182,8 @@ func TestBinderSetWithProperType(t *testing.T) { assert.Error(t, setWithProperType(typ.Field(0).Type.Kind(), "5", val.Field(0))) } -func TestBinderSetFields(t *testing.T) { - ts := new(binderTestStruct) +func TestBindSetFields(t *testing.T) { + ts := new(bindTestStruct) val := reflect.ValueOf(ts).Elem() // Int if assert.NoError(t, setIntField("5", 0, val.FieldByName("I"))) { @@ -225,7 +222,7 @@ func TestBinderSetFields(t *testing.T) { } } -func assertBinderTestStruct(t *testing.T, ts *binderTestStruct) { +func assertBindTestStruct(t *testing.T, ts *bindTestStruct) { assert.Equal(t, 0, ts.I) assert.Equal(t, int8(8), ts.I8) assert.Equal(t, int16(16), ts.I16) @@ -243,7 +240,7 @@ func assertBinderTestStruct(t *testing.T, ts *binderTestStruct) { assert.Equal(t, "", ts.GetCantSet()) } -func testBinderOkay(t *testing.T, r io.Reader, ctype string) { +func testBindOkay(t *testing.T, r io.Reader, ctype string) { e := New() req, _ := http.NewRequest(POST, "/", r) rec := httptest.NewRecorder() @@ -257,7 +254,7 @@ func testBinderOkay(t *testing.T, r io.Reader, ctype string) { } } -func testBinderError(t *testing.T, r io.Reader, ctype string) { +func testBindError(t *testing.T, r io.Reader, ctype string) { e := New() req, _ := http.NewRequest(POST, "/", r) rec := httptest.NewRecorder() diff --git a/logger.go b/log.go similarity index 100% rename from logger.go rename to log.go diff --git a/website/content/guide.md b/website/content/guide.md index 4a6c9a27..d3419973 100644 --- a/website/content/guide.md +++ b/website/content/guide.md @@ -172,9 +172,9 @@ ls avatar.png ### Handling Request -- Bind `JSON`, `XML`, `form` or `query` payload into Go struct based on `Content-Type` request header. -- Render response as `JSON` or `XML` with status code. -- Use the [BindUnmarshaler](https://godoc.org/github.com/labstack/echo#ParamUnmarshaler) interface to bind custom data types for `form` or `query` payloads. The standard [json.Unmarshaler](https://golang.org/pkg/encoding/json/#Unmarshaler) and [xml.Unmarshaler](https://golang.org/pkg/encoding/xml/#Unmarshaler) can of course be used for JSON and XML payloads, respectively. +- Bind `json`, `xml`, `form` or `query` payload into Go struct based on `Content-Type` +request header. +- Render response as `json` or `xml` with status code. ```go type User struct { diff --git a/website/content/guide/customization.md b/website/content/guide/customization.md index 2ec825ab..d47d8067 100644 --- a/website/content/guide/customization.md +++ b/website/content/guide/customization.md @@ -9,35 +9,27 @@ description = "Customizing Echo" ## HTTP Error Handler -Default HTTP error handler sends an error as JSON with the following rules: +`Echo#HTTPErrorHandler` can be used to set custom http error handler. -- If error is `Echo#HTTPError` it sends HTTP response with status code `HTTPError.Code` -and message `HTTPError.Message`. -- If error is `error` it sends HTTP response with status code `500 - Internal Server Error` -and message `error.Error()`. -- It logs the error. - -You can set a custom HTTP error handler using `Echo#HTTPErrorHandler`. +[Learn more](/guide/error-handling) ## Debugging -`Echo#Debug` enable/disable debug mode. +`Echo#Debug` can be used to enable / disable debug mode. ## Logging ### Log Output -`Echo#Logger.SetOutput(io.Writer)` sets the output destination for the logger. -Default value `os.Stdout` +`Echo#Logger.SetOutput(io.Writer)` can be used to set the output destination for +the logger. Default value is `os.Stdout` To completely disable logs use `Echo#Logger.SetOutput(io.Discard)` or `Echo#Logger.SetLevel(log.OFF)` ### Log Level -`Echo#Logger.SetLevel(log.Lvl)` - -SetLogLevel sets the log level for the logger. Default value `OFF`. -Possible values: +`Echo#Logger.SetLevel(log.Lvl)` can be used to set the log level for the logger. +Default value `OFF`. Possible values: - `DEBUG` - `INFO` @@ -45,4 +37,6 @@ Possible values: - `ERROR` - `OFF` -You can also set a custom logger using `Echo#Logger`. +Logging is implemented using `echo.Logger` interface which allows you to use a +custom logger. Custom logger can be set using `Echo#Logger`. + diff --git a/website/content/guide/request.md b/website/content/guide/request.md index 9bfac941..89215879 100644 --- a/website/content/guide/request.md +++ b/website/content/guide/request.md @@ -107,6 +107,20 @@ curl \ -d 'name=Joe' ``` +To bind a custom data type, you can implement `Echo#BindUnmarshaler` interface. + +*Example* + +```go +type Timestamp time.Time + +func (t *Timestamp) UnmarshalParam(src string) error { + ts, err := time.Parse(time.RFC3339, src) + *t = Timestamp(ts) + return err +} +``` + ### Query Parameters Query parameters can be retrieved by name using `Context#QueryParam(name string)`. @@ -127,6 +141,8 @@ curl \ http://localhost:1323\?name\=Joe ``` +Similar to form data, custom data type can be bind using `Context#QueryParam(name string)`. + ### Path Parameters Registered path parameters can be retrieved by name using `Context#Param(name string) string`.