What happened:
Default json Codec fails to unmarshal string-formatted int64 fields in application/json requests
In standard Protobuf JSON mapping (proto3 specification), int64 values are allowed and recommended to be serialized/unserialized as JSON strings (e.g., "id": "123" instead of "id": 123) to prevent precision loss in 64-bit JavaScript numbers.
In Kratos v3, when web clients (e.g., Axios, Vue, React, Postman) send HTTP POST requests with Content-Type: application/json, attempting to send "id": "123" for a Protobuf int64 field results in the following unmarshaling error:
body unmarshal json: cannot unmarshal string into Go struct field MyRequest.id of type int64
Simply importing _ "github.com/go-kratos/kratos/v3/encoding/protojson" in main.go (as suggested in issue #3820) does NOT resolve this issue.
What you expected to happen:
How to reproduce it (as minimally and precisely as possible):
Anything else we need to know?:
Root Cause Analysis
protojson codec registers itself under the name "protojson":
// github.com/go-kratos/kratos/v3/encoding/protojson
const Name = "protojson"
- When a client sends
Content-Type: application/json, Kratos HTTP Server resolves the codec name as "json" via CodecForRequest(r, "Content-Type").
- As a result, Kratos falls back to the default Go standard library
encoding/json codec (registered under "json"), which strictly rejects string-to-int64 unmarshaling for struct fields. The imported "protojson" codec is never invoked.
Solution / Recommended Workaround
Customizing http.RequestDecoder on the http.Server options ensures that any proto.Message request payload is unmarshaled using protojson.UnmarshalOptions (which natively supports string-formatted int64), regardless of whether the HTTP Content-Type is application/json or application/protojson.
Implementation
package encoder
import (
"encoding/json"
"io"
"net/http"
kerrors "github.com/go-kratos/kratos/v3/errors"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
)
// CustomRequestDecoder handles both application/json and application/protojson smoothly,
// supporting string-formatted int64 fields (e.g., "id": "123") without forcing clients to change Content-Type.
func CustomRequestDecoder(r *http.Request, v any) error {
data, err := io.ReadAll(r.Body)
if err != nil {
return kerrors.BadRequest("BODY", err.Error())
}
if m, ok := v.(proto.Message); ok {
if err := (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal(data, m); err != nil {
return kerrors.BadRequest("BODY", err.Error())
}
return nil
}
if err := json.Unmarshal(data, v); err != nil {
return kerrors.BadRequest("BODY", err.Error())
}
return nil
}
Server Configuration
srv := http.NewServer(
http.Middleware(...),
http.RequestDecoder(encoder.CustomRequestDecoder),
)
Suggestion for Kratos Framework
Consider updating the default json codec in github.com/go-kratos/kratos/v3/encoding/json or the default HTTP request decoder to check if the target struct is proto.Message, and fallback to protojson unmarshaling rules for application/json content types.
Environment:
- Kratos version (use
kratos -v): v3.0.0
- Go version (use
go version): 1.25.11
- OS (e.g:
cat /etc/os-release):
- Others:
What happened:
Default
jsonCodec fails to unmarshal string-formattedint64fields inapplication/jsonrequestsIn standard Protobuf JSON mapping (proto3 specification),
int64values are allowed and recommended to be serialized/unserialized as JSON strings (e.g.,"id": "123"instead of"id": 123) to prevent precision loss in 64-bit JavaScript numbers.In Kratos v3, when web clients (e.g., Axios, Vue, React, Postman) send HTTP POST requests with
Content-Type: application/json, attempting to send"id": "123"for a Protobufint64field results in the following unmarshaling error:Simply importing
_ "github.com/go-kratos/kratos/v3/encoding/protojson"inmain.go(as suggested in issue #3820) does NOT resolve this issue.What you expected to happen:
How to reproduce it (as minimally and precisely as possible):
Anything else we need to know?:
Root Cause Analysis
protojsoncodec registers itself under the name"protojson":Content-Type: application/json, Kratos HTTP Server resolves the codec name as"json"viaCodecForRequest(r, "Content-Type").encoding/jsoncodec (registered under"json"), which strictly rejects string-to-int64 unmarshaling for struct fields. The imported"protojson"codec is never invoked.Solution / Recommended Workaround
Customizing
http.RequestDecoderon thehttp.Serveroptions ensures that anyproto.Messagerequest payload is unmarshaled usingprotojson.UnmarshalOptions(which natively supports string-formattedint64), regardless of whether the HTTPContent-Typeisapplication/jsonorapplication/protojson.Implementation
Server Configuration
Suggestion for Kratos Framework
Consider updating the default
jsoncodec ingithub.com/go-kratos/kratos/v3/encoding/jsonor the default HTTP request decoder to check if the target struct isproto.Message, and fallback toprotojsonunmarshaling rules forapplication/jsoncontent types.Environment:
kratos -v): v3.0.0go version): 1.25.11cat /etc/os-release):