Repository: cloudwego/hertz
Pull request #684 (2023-03-22 08:45:55)

feat: support peek multi value for query args
#### 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
-->
feat

#### 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:
-->
- [x] This PR title match the format: \<type\>(optional scope): \<description\>
- [x] The description of this PR title is user-oriented and clear enough for others to understand.


#### (Optional) Translate the PR title into Chinese.
支持获取多个值的query参数

#### (Optional) More detail description for this PR(en: English/zh: Chinese).
<!--
Provide more detailed info for review. If it is a perf type PR, perf data is suggested to give.
-->
en: 
`ctx.Query` cannot get multiple values of `a` from `a=1&a=2&b=1`.
`QueryArray` is provided in `gin` to get multiple values, but [rfc3986](https://www.rfc-editor.org/rfc/rfc3986#section-3.4) does not define the implementation of the server, and many frameworks are inconsistent when parsing repeated queries , some only use the first or last occurrence.
So I'm not sure it needs to be implemented in `RequestContext`, but I hope there is still a way to get it.
```go
 ctx.QueryArgs().PeekMulti("a")
```
zh(optional):
`ctx.Query` 无法从 `a=1&a=2&b=1` 获取 `a` 的多个值。
`gin`中提供了`QueryArray`获取多个值，但是 [rfc3986](https://www.rfc-editor.org/rfc/rfc3986#section-3.4) 没有定义服务端的实现，很多框架在解析的时候不一致，有些只使用第一个值或最后一个值。
所以我不确定它是否需要在`RequestContext`中实现，但我希望有办法获到它。
```go
 ctx.QueryArgs().PeekMulti("a")
```

#### Which issue(s) this PR fixes:
<!--
Automatically closes linked issue when PR is merged.
Eg: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->


li-jin-gou: refer to https://github.com/cloudwego/hertz/pull/569/files and do you have a real world example of this use?
a631807682: > refer to [#569 (files)](https://github.com/cloudwego/hertz/pull/569/files) and do you have a real world example of this use?

Do you mean I need to describe this scenario in a test like https://github.com/cloudwego/hertz/pull/569/files#diff-6b0ef71f167bc3e1767a55944dc08d8cfb025629780b5cfcceb1cbe21e668e21R577 ?
Or do I need to add relevant code samples in the PR's description?
li-jin-gou: > > refer to [#569 (files)](https://github.com/cloudwego/hertz/pull/569/files) and do you have a real world example of this use?
> 
> Do you mean I need to describe this scenario in a test like https://github.com/cloudwego/hertz/pull/569/files#diff-6b0ef71f167bc3e1767a55944dc08d8cfb025629780b5cfcceb1cbe21e668e21R577 ? Or do I need to add relevant code samples in the PR's description?

Reference code implementation
li-jin-gou: > > > > refer to [#569 (files)](https://github.com/cloudwego/hertz/pull/569/files) and do you have a real world example of this use?
> > > 
> > > 
> > > Do you mean I need to describe this scenario in a test like [#569 (files)](https://github.com/cloudwego/hertz/pull/569/files#diff-6b0ef71f167bc3e1767a55944dc08d8cfb025629780b5cfcceb1cbe21e668e21R577) ? Or do I need to add relevant code samples in the PR's description?
> > 
> > 
> > Reference code implementation
> 
> Sorry, I still don't get your point, can you tell me what aspect of PR implementation I need to refer to?
> 
> 1. Is it about api design?
>    We need to provide this api on RequestContext? There are two different considerations here, and the current implementation is the first one, please let me know if there is any problem.
>    [Added ctx.QueryMulti() gofiber/fiber#288 (comment)](https://github.com/gofiber/fiber/pull/288#issuecomment-618739905)
>    [Implement QueryArray and PostFormArray methods gin-gonic/gin#570](https://github.com/gin-gonic/gin/pull/570)
> 2. Are there performance concerns?
>    QueryArgs will only be parsed once, this method is not much different from [GetQuery](https://github.com/cloudwego/hertz/blob/develop/pkg/app/context.go#L1229)
> 3. Did not describe the real usage scenario?
>    Do I need to add tests for multiple parameter requests to describe it?
> 
> The above is all my guesswork, I'd like some clear advice to let me know what I should do. 🙏

ok
li-jin-gou: users needs to use this PR and I will CR it today

Patch:
diff --git a/pkg/protocol/args.go b/pkg/protocol/args.go
index f29aefe..fa2a670 100644
--- a/pkg/protocol/args.go
+++ b/pkg/protocol/args.go
@@ -379,6 +379,17 @@ func (a *Args) PeekExists(key string) (string, bool) {
 	return peekArgStrExists(a.args, key)
 }
 
+// PeekAll returns all the arg values for the given key.
+func (a *Args) PeekAll(key string) [][]byte {
+	var values [][]byte
+	a.VisitAll(func(k, v []byte) {
+		if bytesconv.B2s(k) == key {
+			values = append(values, v)
+		}
+	})
+	return values
+}
+
 func visitArgs(args []argsKV, f func(k, v []byte)) {
 	for i, n := 0, len(args); i < n; i++ {
 		kv := &args[i]


Test patch:
diff --git a/pkg/protocol/args_test.go b/pkg/protocol/args_test.go
index 6a613dc..0e89f30 100644
--- a/pkg/protocol/args_test.go
+++ b/pkg/protocol/args_test.go
@@ -129,3 +129,26 @@ func TestArgsVisitAll(t *testing.T) {
 	})
 	assert.DeepEqual(t, []string{"cloudwego", "hertz", "hello", "world"}, s)
 }
+
+func TestArgsPeekMulti(t *testing.T) {
+	var a Args
+	a.Add("cloudwego", "hertz")
+	a.Add("cloudwego", "kitex")
+	a.Add("cloudwego", "")
+	a.Add("hello", "world")
+
+	vv := a.PeekAll("cloudwego")
+	expectedVV := [][]byte{
+		[]byte("hertz"),
+		[]byte("kitex"),
+		[]byte(nil),
+	}
+	assert.DeepEqual(t, expectedVV, vv)
+
+	vv = a.PeekAll("aaaa")
+	assert.DeepEqual(t, 0, len(vv))
+
+	vv = a.PeekAll("hello")
+	expectedVV = [][]byte{[]byte("world")}
+	assert.DeepEqual(t, expectedVV, vv)
+}