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>
61 lines
2.0 KiB
Go
61 lines
2.0 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 (
|
|
"github.com/gin-gonic/gin/codec/json"
|
|
)
|
|
|
|
// Problem represents an RFC 9457 problem details object.
|
|
// See https://www.rfc-editor.org/rfc/rfc9457 for the meaning of each member.
|
|
type Problem struct {
|
|
// Type is a URI reference that identifies the problem type.
|
|
// When empty it is omitted from the output, which consumers
|
|
// must interpret as "about:blank" (RFC 9457 section 3.1.1).
|
|
Type string `json:"type,omitempty"`
|
|
// Title is a short, human-readable summary of the problem type.
|
|
Title string `json:"title,omitempty"`
|
|
// Status is the HTTP status code generated by the origin server.
|
|
Status int `json:"status,omitempty"`
|
|
// Detail is a human-readable explanation specific to this occurrence.
|
|
Detail string `json:"detail,omitempty"`
|
|
// Instance is a URI reference that identifies the specific occurrence.
|
|
Instance string `json:"instance,omitempty"`
|
|
// Extensions holds problem type specific extension members
|
|
// (RFC 9457 section 3.2). They are serialized as top-level members
|
|
// of the problem details object. Extension keys that collide with
|
|
// a standard member name are ignored.
|
|
Extensions map[string]any `json:"-"`
|
|
}
|
|
|
|
// MarshalJSON implements the json.Marshaler interface, serializing
|
|
// Extensions as top-level members of the problem details object.
|
|
func (p Problem) MarshalJSON() ([]byte, error) {
|
|
type problem Problem // avoid infinite recursion
|
|
if len(p.Extensions) == 0 {
|
|
return json.API.Marshal(problem(p))
|
|
}
|
|
obj := make(map[string]any, len(p.Extensions)+5)
|
|
for k, v := range p.Extensions {
|
|
obj[k] = v
|
|
}
|
|
if p.Type != "" {
|
|
obj["type"] = p.Type
|
|
}
|
|
if p.Title != "" {
|
|
obj["title"] = p.Title
|
|
}
|
|
if p.Status != 0 {
|
|
obj["status"] = p.Status
|
|
}
|
|
if p.Detail != "" {
|
|
obj["detail"] = p.Detail
|
|
}
|
|
if p.Instance != "" {
|
|
obj["instance"] = p.Instance
|
|
}
|
|
return json.API.Marshal(obj)
|
|
}
|