Repository: cloudwego/hertz
Pull request #965 (2023-09-27 08:48:49)

fix: customized type decode for binding
#### What type of PR is this?
<!--
Add one of the following kinds:

build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
docs: Documentation only changes
feat: A new feature
optimize: A new optimization
fix: A bug fix
perf: A code change that improves performance
refactor: A code change that neither fixes a bug nor adds a feature
style: Changes that do not affect the meaning of the code (white space, formatting, missing semi-colons, etc)
test: Adding missing tests or correcting existing tests
chore: Changes to the build process or auxiliary tools and libraries such as documentation generation
-->
fix
#### Check the PR title.
<!--
The description of the title will be attached in Release Notes, 
so please describe it from user-oriented, what this PR does / why we need it.
Please check your PR title with the below requirements:
-->
- [ ] This PR title match the format: \<type\>(optional scope): \<description\>
- [ ] The description of this PR title is user-oriented and clear enough for others to understand.
- [ ] Attach the PR updating the user documentation if the current PR requires user awareness at the usage level. [User docs repo](https://github.com/cloudwego/cloudwego.github.io)


#### (Optional) Translate the PR title into Chinese.
修复自定义绑定函数带来的 time.Time 绑定不一致的行为

#### (Optional) More detailed description for this PR(en: English/zh: Chinese).
<!--
Provide more detailed info for review(e.g., it's recommended to provide perf data if this is a perf type PR).
-->
en: Fix inconsistent behavior of time.Time bindings due to custom binding functions
zh(optional): 修复自定义绑定函数带来的 time.Time 绑定不一致的行为
之前的绑定行为是必须有对应的 tag 才会去执行，自定义校验函数，例如下方结构体，只有请求的 query 有 "A" 的时候才会去执行自定义绑定函数。
```
type A struct {
     A  time.Time `query:"A"`
}
```
重构后，修改了这一行为，认为 "只要定义了自定义绑定函数就一定会执行，并且会将请求内容传给用户，让其自行取值"，所以自带的 time.Time 的自定义方法会将用户 json 已经解析过的 time.Time 覆盖掉。
这块还是改回只有 tag 存在才执行自定义方法，避免出现其他覆盖的行为

#### (Optional) Which issue(s) this PR fixes:
<!--
Automatically closes linked issue when PR is merged.
Eg: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
https://github.com/cloudwego/hertz/issues/964
#### (Optional) The PR that updates user documentation:
<!--
If the current PR requires user awareness at the usage level, please submit a PR to update user docs. [User docs repo](https://github.com/cloudwego/cloudwego.github.io)
-->

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/cloudwego/hertz/pull/965?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) Report
All modified lines are covered by tests :white_check_mark:
> Comparison is base [(`18af58f`)](https://app.codecov.io/gh/cloudwego/hertz/commit/18af58fabfb0930c4e882478508ad413e0fd8d04?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) 81.87% compared to head [(`09b5509`)](https://app.codecov.io/gh/cloudwego/hertz/pull/965?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) 81.85%.
> Report is 1 commits behind head on develop.

> :exclamation: Current head 09b5509 differs from pull request most recent head da33d15. Consider uploading reports for the commit da33d15 to get more accurate results

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##           develop     #965      +/-   ##
===========================================
- Coverage    81.87%   81.85%   -0.03%     
===========================================
  Files           98       98              
  Lines         9926     9926              
===========================================
- Hits          8127     8125       -2     
- Misses        1310     1311       +1     
- Partials       489      490       +1     
```


[see 1 file with indirect coverage changes](https://app.codecov.io/gh/cloudwego/hertz/pull/965/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego)


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/cloudwego/hertz/pull/965?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego).


Patch:
diff --git a/pkg/app/server/binding/internal/decoder/customized_type_decoder.go b/pkg/app/server/binding/internal/decoder/customized_type_decoder.go
index 8bf0f01..19efa46 100644
--- a/pkg/app/server/binding/internal/decoder/customized_type_decoder.go
+++ b/pkg/app/server/binding/internal/decoder/customized_type_decoder.go
@@ -69,6 +69,9 @@ func (d *customizedFieldTextDecoder) Decode(req *protocol.Request, params param.
 			break
 		}
 	}
+	if !exist {
+		return nil
+	}
 	if len(text) == 0 && len(defaultValue) != 0 {
 		text = defaultValue
 	}
@@ -77,6 +80,9 @@ func (d *customizedFieldTextDecoder) Decode(req *protocol.Request, params param.
 	if err != nil {
 		return err
 	}
+	if !v.IsValid() {
+		return nil
+	}
 
 	reqValue = GetFieldValue(reqValue, d.parentIndex)
 	field := reqValue.Field(d.index)


Test patch:
diff --git a/pkg/app/server/binding/binder_test.go b/pkg/app/server/binding/binder_test.go
index c971776..2a66229 100644
--- a/pkg/app/server/binding/binder_test.go
+++ b/pkg/app/server/binding/binder_test.go
@@ -47,6 +47,7 @@ import (
 	"net/url"
 	"reflect"
 	"testing"
+	"time"
 
 	"github.com/cloudwego/hertz/pkg/app/server/binding/testdata"
 	"github.com/cloudwego/hertz/pkg/common/test/assert"
@@ -526,7 +527,7 @@ type CustomizedDecode struct {
 
 func TestBind_CustomizedTypeDecode(t *testing.T) {
 	type Foo struct {
-		F ***CustomizedDecode
+		F ***CustomizedDecode `query:"a"`
 	}
 
 	bindConfig := &BindConfig{}
@@ -1491,6 +1492,29 @@ func Test_ValidatorErrorFactory(t *testing.T) {
 	assert.DeepEqual(t, "validateErr: expr_path=[validateFailField]: B, cause=[validateErrMsg]: ", err.Error())
 }
 
+// Test_Issue964 used to the cover issue for time.Time
+func Test_Issue964(t *testing.T) {
+	type CreateReq struct {
+		StartAt *time.Time `json:"startAt"`
+	}
+	r := newMockRequest().SetBody([]byte("{\n  \"startAt\": \"2006-01-02T15:04:05+07:00\"\n}")).SetJSONContentType()
+	var req CreateReq
+	err := DefaultBinder().BindAndValidate(r.Req, &req, nil)
+	if err != nil {
+		t.Error(err)
+	}
+	assert.DeepEqual(t, "2006-01-02 15:04:05 +0700 +0700", req.StartAt.String())
+	r = newMockRequest()
+	req = CreateReq{}
+	err = DefaultBinder().BindAndValidate(r.Req, &req, nil)
+	if err != nil {
+		t.Error(err)
+	}
+	if req.StartAt != nil {
+		t.Error("expected nil")
+	}
+}
+
 func Benchmark_Binding(b *testing.B) {
 	type Req struct {
 		Version string `path:"v"`