Skip to content

Commit 3f9e3e3

Browse files
authored
Merge pull request #417 from labstack/docs-binding-slices-jwt-note
docs: slice binding example (#170) + JWT timing-attack caveat (#100)
2 parents 1a0a436 + af934b7 commit 3f9e3e3

10 files changed

Lines changed: 104 additions & 5 deletions

File tree

site/src/content/docs/cookbook/jwt.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ func login(c *echo.Context) error {
4747
username := c.FormValue("username")
4848
password := c.FormValue("password")
4949

50-
// Throws unauthorized error
50+
// Demo only: hard-coded credentials compared in plain text. In production,
51+
// look up the user and verify a hashed password with a constant-time compare
52+
// (e.g. golang.org/x/crypto/bcrypt's CompareHashAndPassword) to avoid timing attacks.
5153
if username != "jon" || password != "shhh!" {
5254
return echo.ErrUnauthorized
5355
}

site/src/content/docs/es/cookbook/jwt.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ func login(c *echo.Context) error {
4747
username := c.FormValue("username")
4848
password := c.FormValue("password")
4949

50-
// Throws unauthorized error
50+
// Demo only: hard-coded credentials compared in plain text. In production,
51+
// look up the user and verify a hashed password with a constant-time compare
52+
// (e.g. golang.org/x/crypto/bcrypt's CompareHashAndPassword) to avoid timing attacks.
5153
if username != "jon" || password != "shhh!" {
5254
return echo.ErrUnauthorized
5355
}

site/src/content/docs/es/guide/binding.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,24 @@ if err := c.Bind(&user); err != nil {
4444
Los campos de path, query, header y form requieren un **tag explícito**. JSON y XML usan
4545
el nombre del campo del struct cuando se omite el tag, igual que la biblioteca estándar.
4646

47+
### Slices
48+
49+
Los valores repetidos de query, path, formulario o header se enlazan a un campo
50+
slice, que recoge cada aparición:
51+
52+
```go
53+
// GET /search?tag=go&tag=web&tag=api
54+
type Filter struct {
55+
Tags []string `query:"tag"`
56+
}
57+
58+
var f Filter
59+
if err := c.Bind(&f); err != nil {
60+
return c.String(http.StatusBadRequest, "bad request")
61+
}
62+
// f.Tags == []string{"go", "web", "api"}
63+
```
64+
4765
### Content types del body
4866

4967
Al decodificar el body del request, el header `Content-Type` selecciona el decoder:

site/src/content/docs/guide/binding.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,24 @@ if err := c.Bind(&user); err != nil {
4444
Path, query, header, and form fields require an **explicit tag**. JSON and XML fall
4545
back to the struct field name when the tag is omitted, matching the standard library.
4646

47+
### Slices
48+
49+
Repeated query, path, form, or header values bind to a slice field — the field
50+
collects every occurrence:
51+
52+
```go
53+
// GET /search?tag=go&tag=web&tag=api
54+
type Filter struct {
55+
Tags []string `query:"tag"`
56+
}
57+
58+
var f Filter
59+
if err := c.Bind(&f); err != nil {
60+
return c.String(http.StatusBadRequest, "bad request")
61+
}
62+
// f.Tags == []string{"go", "web", "api"}
63+
```
64+
4765
### Body content types
4866

4967
When decoding the request body, the `Content-Type` header selects the decoder:

site/src/content/docs/ja/cookbook/jwt.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ func login(c *echo.Context) error {
4646
username := c.FormValue("username")
4747
password := c.FormValue("password")
4848

49-
// Throws unauthorized error
49+
// Demo only: hard-coded credentials compared in plain text. In production,
50+
// look up the user and verify a hashed password with a constant-time compare
51+
// (e.g. golang.org/x/crypto/bcrypt's CompareHashAndPassword) to avoid timing attacks.
5052
if username != "jon" || password != "shhh!" {
5153
return echo.ErrUnauthorized
5254
}

site/src/content/docs/ja/guide/binding.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,24 @@ if err := c.Bind(&user); err != nil {
4646
JSON と XML はタグが省略された場合、標準ライブラリと同じように struct フィールド名へ
4747
フォールバックします。
4848

49+
### スライス
50+
51+
繰り返し指定されたクエリ・パス・フォーム・ヘッダーの値はスライスフィールドにバインドされ、
52+
すべての出現が収集されます:
53+
54+
```go
55+
// GET /search?tag=go&tag=web&tag=api
56+
type Filter struct {
57+
Tags []string `query:"tag"`
58+
}
59+
60+
var f Filter
61+
if err := c.Bind(&f); err != nil {
62+
return c.String(http.StatusBadRequest, "bad request")
63+
}
64+
// f.Tags == []string{"go", "web", "api"}
65+
```
66+
4967
### ボディのコンテンツタイプ
5068

5169
リクエストボディをデコードするときは、`Content-Type` header によってデコーダーが選ばれます。

site/src/content/docs/pt-br/cookbook/jwt.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ func login(c *echo.Context) error {
4747
username := c.FormValue("username")
4848
password := c.FormValue("password")
4949

50-
// Throws unauthorized error
50+
// Demo only: hard-coded credentials compared in plain text. In production,
51+
// look up the user and verify a hashed password with a constant-time compare
52+
// (e.g. golang.org/x/crypto/bcrypt's CompareHashAndPassword) to avoid timing attacks.
5153
if username != "jon" || password != "shhh!" {
5254
return echo.ErrUnauthorized
5355
}

site/src/content/docs/pt-br/guide/binding.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,24 @@ if err := c.Bind(&user); err != nil {
4444
Campos de path, query, header e form exigem uma **tag explícita**. JSON e XML usam
4545
o nome do campo da struct quando a tag é omitida, igual à biblioteca padrão.
4646

47+
### Slices
48+
49+
Valores repetidos de query, path, formulário ou header são vinculados a um campo
50+
slice, que coleta cada ocorrência:
51+
52+
```go
53+
// GET /search?tag=go&tag=web&tag=api
54+
type Filter struct {
55+
Tags []string `query:"tag"`
56+
}
57+
58+
var f Filter
59+
if err := c.Bind(&f); err != nil {
60+
return c.String(http.StatusBadRequest, "bad request")
61+
}
62+
// f.Tags == []string{"go", "web", "api"}
63+
```
64+
4765
### Tipos de conteúdo do body
4866

4967
Ao decodificar o body do request, o header `Content-Type` seleciona o decoder:

site/src/content/docs/zh-cn/cookbook/jwt.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ func login(c *echo.Context) error {
4646
username := c.FormValue("username")
4747
password := c.FormValue("password")
4848

49-
// Throws unauthorized error
49+
// Demo only: hard-coded credentials compared in plain text. In production,
50+
// look up the user and verify a hashed password with a constant-time compare
51+
// (e.g. golang.org/x/crypto/bcrypt's CompareHashAndPassword) to avoid timing attacks.
5052
if username != "jon" || password != "shhh!" {
5153
return echo.ErrUnauthorized
5254
}

site/src/content/docs/zh-cn/guide/binding.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,23 @@ if err := c.Bind(&user); err != nil {
4444
路径、查询、header 和表单字段都需要**显式标签**。JSON 和 XML 在省略标签时会回退到
4545
struct 字段名,这与标准库的行为一致。
4646

47+
### 切片
48+
49+
重复的查询、路径、表单或请求头值会绑定到切片字段——该字段会收集每一个值:
50+
51+
```go
52+
// GET /search?tag=go&tag=web&tag=api
53+
type Filter struct {
54+
Tags []string `query:"tag"`
55+
}
56+
57+
var f Filter
58+
if err := c.Bind(&f); err != nil {
59+
return c.String(http.StatusBadRequest, "bad request")
60+
}
61+
// f.Tags == []string{"go", "web", "api"}
62+
```
63+
4764
### 请求体内容类型
4865

4966
解码请求体时,`Content-Type` header 会选择对应的解码器:

0 commit comments

Comments
 (0)