// 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) }