这是indexloc提供的服务,不要输入任何密码
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/docs/features/request-inputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ The following special types are supported out of the box:
| `time.Time` | `{"type": "string", "format": "date-time"}` | `"2020-01-01T12:00:00Z"` |
| `url.URL` | `{"type": "string", "format": "uri"}` | `"https://example.com"` |
| `net.IP` | `{"type": "string", "format": "ipv4"}` | `"127.0.0.1"` |
| `netip.Addr` | `{"type": "string", "format": "ipv4"}` | `"127.0.0.1"` |
| `netip.Addr` | `{"type": "string", "format": "ip"}` | `"127.0.0.1"` or `fe80::1` |
| `json.RawMessage` | `{}` | `["whatever", "you", "want"]` |

You can override this default behavior if needed as described in [Schema Customization](./schema-customization.md) and [Request Validation](./request-validation.md), e.g. setting a custom `format` tag for IPv6.
Expand Down
2 changes: 1 addition & 1 deletion schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ func schemaFromType(r Registry, t reflect.Type) *Schema {
case ipType:
return &Schema{Type: TypeString, Nullable: isPointer, Format: "ipv4"}
case ipAddrType:
return &Schema{Type: TypeString, Nullable: isPointer, Format: "ipv4"}
return &Schema{Type: TypeString, Nullable: isPointer, Format: "ip"}
case rawMessageType:
return &Schema{}
}
Expand Down
2 changes: 1 addition & 1 deletion schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func TestSchema(t *testing.T) {
{
name: "ipAddr",
input: netip.AddrFrom4([4]byte{127, 0, 0, 1}),
expected: `{"type": "string", "format": "ipv4"}`,
expected: `{"type": "string", "format": "ip"}`,
},
{
name: "json.RawMessage",
Expand Down
5 changes: 5 additions & 0 deletions validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"math"
"net"
"net/mail"
"net/netip"
"net/url"
"reflect"
"regexp"
Expand Down Expand Up @@ -247,6 +248,10 @@ func validateFormat(path *PathBuffer, str string, s *Schema, res *ValidateResult
if ip := net.ParseIP(str); ip == nil || ip.To16() == nil {
res.Add(path, str, validation.MsgExpectedRFC2373IPv6)
}
case "ip":
if _, err := netip.ParseAddr(str); err != nil {
res.Add(path, str, validation.MsgExpectedRFCIPAddr)
}
Comment on lines +251 to +254
Copy link
Owner

@danielgtaylor danielgtaylor Apr 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@costela I love this! One concern I have is that ipv4 and ipv6 are actually in the JSON Schema spec but ip is not. See https://json-schema.org/understanding-json-schema/reference/type#ip-addresses.

I wonder if we should support all three? The two standard ones, and then ip for either one since Go supports it well. What do you think?

edit: just saw your other comment which is similar to mine, sorry 😂

case "uri", "uri-reference", "iri", "iri-reference":
if _, err := url.Parse(str); err != nil {
res.Add(path, str, ErrorFormatter(validation.MsgExpectedRFC3986URI, err))
Expand Down
22 changes: 22 additions & 0 deletions validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,28 @@ var validateTests = []struct {
input: map[string]any{"value": "1234"},
errs: []string{"expected string to be RFC 2373 ipv6"},
},
{
name: "ipv4 success",
typ: reflect.TypeOf(struct {
Value string `json:"value" format:"ip"`
}{}),
input: map[string]any{"value": "127.0.0.1"},
},
{
name: "ipv6 success",
typ: reflect.TypeOf(struct {
Value string `json:"value" format:"ip"`
}{}),
input: map[string]any{"value": "2001:0db8:85a3:0000:0000:8a2e:0370:7334"},
},
{
name: "expected ipv4 or ipv6",
typ: reflect.TypeOf(struct {
Value string `json:"value" format:"ip"`
}{}),
input: map[string]any{"value": "1234"},
errs: []string{"expected string to be either RFC 2673 ipv4 or RFC 2373 ipv6"},
},
{
name: "uri success",
typ: reflect.TypeOf(struct {
Expand Down
1 change: 1 addition & 0 deletions validation/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var (
MsgExpectedRFC5890Hostname = "expected string to be RFC 5890 hostname"
MsgExpectedRFC2673IPv4 = "expected string to be RFC 2673 ipv4"
MsgExpectedRFC2373IPv6 = "expected string to be RFC 2373 ipv6"
MsgExpectedRFCIPAddr = "expected string to be either RFC 2673 ipv4 or RFC 2373 ipv6"
MsgExpectedRFC3986URI = "expected string to be RFC 3986 uri: %v"
MsgExpectedRFC4122UUID = "expected string to be RFC 4122 uuid: %v"
MsgExpectedRFC6570URITemplate = "expected string to be RFC 6570 uri-template"
Expand Down