mirror of
https://github.com/gin-gonic/gin.git
synced 2026-09-05 07:02:15 +08:00
Add first-class support for RFC 9457 "Problem Details for HTTP APIs" error responses in JSON format: - gin.Problem type with the standard members (type, title, status, detail, instance) and extension members serialized at the top level - render.ProblemJSON renderer setting Content-Type to "application/problem+json; charset=utf-8" - Context.ProblemJSON and Context.AbortWithProblemJSON helpers; when given a gin.Problem, a zero Status defaults to the response code and an empty Title defaults to http.StatusText Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
62 lines
1.6 KiB
Go
62 lines
1.6 KiB
Go
// Copyright 2026 Gin Core Team. All rights reserved.
|
|
// Use of this source code is governed by a MIT style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package gin
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin/codec/json"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestProblemMarshalJSON(t *testing.T) {
|
|
p := Problem{
|
|
Type: "https://example.com/probs/out-of-credit",
|
|
Title: "You do not have enough credit.",
|
|
Status: 403,
|
|
Detail: "Your current balance is 30, but that costs 50.",
|
|
Instance: "/account/12345/msgs/abc",
|
|
}
|
|
|
|
jsonBytes, err := json.API.Marshal(p)
|
|
|
|
require.NoError(t, err)
|
|
assert.JSONEq(t, `{
|
|
"type": "https://example.com/probs/out-of-credit",
|
|
"title": "You do not have enough credit.",
|
|
"status": 403,
|
|
"detail": "Your current balance is 30, but that costs 50.",
|
|
"instance": "/account/12345/msgs/abc"
|
|
}`, string(jsonBytes))
|
|
}
|
|
|
|
func TestProblemMarshalJSONOmitsEmptyMembers(t *testing.T) {
|
|
jsonBytes, err := json.API.Marshal(Problem{Status: 404})
|
|
|
|
require.NoError(t, err)
|
|
assert.JSONEq(t, `{"status":404}`, string(jsonBytes))
|
|
}
|
|
|
|
func TestProblemMarshalJSONExtensions(t *testing.T) {
|
|
p := Problem{
|
|
Status: 403,
|
|
Detail: "Your current balance is 30, but that costs 50.",
|
|
Extensions: map[string]any{
|
|
"balance": 30,
|
|
"status": "extension members must not override standard members",
|
|
},
|
|
}
|
|
|
|
jsonBytes, err := json.API.Marshal(p)
|
|
|
|
require.NoError(t, err)
|
|
assert.JSONEq(t, `{
|
|
"status": 403,
|
|
"detail": "Your current balance is 30, but that costs 50.",
|
|
"balance": 30
|
|
}`, string(jsonBytes))
|
|
}
|