mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 23:40:35 +01:00
Publish version 11.2.5 | Problem Details for HTTP APIs - #1336
Former-commit-id: 25fa9484d0561f669cd94453a6ea5d156b6997cb
This commit is contained in:
commit
c6099a3c3b
|
@ -21,6 +21,13 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
|
||||||
|
|
||||||
**How to upgrade**: Open your command-line and execute this command: `go get github.com/kataras/iris@master`.
|
**How to upgrade**: Open your command-line and execute this command: `go get github.com/kataras/iris@master`.
|
||||||
|
|
||||||
|
# Mo, 12 August 2019 | v11.2.5
|
||||||
|
|
||||||
|
- [New Feature: Problem Details for HTTP APIs based](https://github.com/kataras/iris/pull/1336)
|
||||||
|
- [Add Context.AbsoluteURI](https://github.com/kataras/iris/pull/1336/files#diff-15cce7299aae8810bcab9b0bf9a2fdb1R2368)
|
||||||
|
|
||||||
|
Commit log: https://github.com/kataras/iris/compare/v11.2.4...v11.2.5
|
||||||
|
|
||||||
# Fr, 09 August 2019 | v11.2.4
|
# Fr, 09 August 2019 | v11.2.4
|
||||||
|
|
||||||
- Fixes [iris.Jet: no view engine found for '.jet' or '.html'](https://github.com/kataras/iris/issues/1327)
|
- Fixes [iris.Jet: no view engine found for '.jet' or '.html'](https://github.com/kataras/iris/issues/1327)
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
11.2.4:https://github.com/kataras/iris/releases/tag/v11.2.4
|
11.2.5:https://github.com/kataras/iris/releases/tag/v11.2.5
|
|
@ -7,12 +7,15 @@ import (
|
||||||
func main() {
|
func main() {
|
||||||
app := iris.New()
|
app := iris.New()
|
||||||
|
|
||||||
|
// Catch a specific error code.
|
||||||
app.OnErrorCode(iris.StatusInternalServerError, func(ctx iris.Context) {
|
app.OnErrorCode(iris.StatusInternalServerError, func(ctx iris.Context) {
|
||||||
ctx.HTML("Message: <b>" + ctx.Values().GetString("message") + "</b>")
|
ctx.HTML("Message: <b>" + ctx.Values().GetString("message") + "</b>")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Catch all error codes [app.OnAnyErrorCode...]
|
||||||
|
|
||||||
app.Get("/", func(ctx iris.Context) {
|
app.Get("/", func(ctx iris.Context) {
|
||||||
ctx.HTML(`Click <a href="/my500">here</a> to fire the 500 status code`)
|
ctx.HTML(`Click <a href="/my500">here</a> to pretend an HTTP error`)
|
||||||
})
|
})
|
||||||
|
|
||||||
app.Get("/my500", func(ctx iris.Context) {
|
app.Get("/my500", func(ctx iris.Context) {
|
||||||
|
@ -24,5 +27,60 @@ func main() {
|
||||||
ctx.Writef("Hello %s", ctx.Params().Get("firstname"))
|
ctx.Writef("Hello %s", ctx.Params().Get("firstname"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app.Get("/product-problem", problemExample)
|
||||||
|
|
||||||
|
app.Get("/product-error", func(ctx iris.Context) {
|
||||||
|
ctx.Writef("explain the error")
|
||||||
|
})
|
||||||
|
|
||||||
|
// http://localhost:8080
|
||||||
|
// http://localhost:8080/my500
|
||||||
|
// http://localhost:8080/u/gerasimos
|
||||||
|
// http://localhost:8080/product-problem
|
||||||
app.Run(iris.Addr(":8080"))
|
app.Run(iris.Addr(":8080"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newProductProblem(productName, detail string) iris.Problem {
|
||||||
|
return iris.NewProblem().
|
||||||
|
// The type URI, if relative it automatically convert to absolute.
|
||||||
|
Type("/product-error").
|
||||||
|
// The title, if empty then it gets it from the status code.
|
||||||
|
Title("Product validation problem").
|
||||||
|
// Any optional details.
|
||||||
|
Detail(detail).
|
||||||
|
// The status error code, required.
|
||||||
|
Status(iris.StatusBadRequest).
|
||||||
|
// Any custom key-value pair.
|
||||||
|
Key("productName", productName)
|
||||||
|
// Optional cause of the problem, chain of Problems.
|
||||||
|
// Cause(iris.NewProblem().Type("/error").Title("cause of the problem").Status(400))
|
||||||
|
}
|
||||||
|
|
||||||
|
func problemExample(ctx iris.Context) {
|
||||||
|
/*
|
||||||
|
p := iris.NewProblem().
|
||||||
|
Type("/validation-error").
|
||||||
|
Title("Your request parameters didn't validate").
|
||||||
|
Detail("Optional details about the error.").
|
||||||
|
Status(iris.StatusBadRequest).
|
||||||
|
Key("customField1", customValue1)
|
||||||
|
Key("customField2", customValue2)
|
||||||
|
ctx.Problem(p)
|
||||||
|
|
||||||
|
// OR
|
||||||
|
ctx.Problem(iris.Problem{
|
||||||
|
"type": "/validation-error",
|
||||||
|
"title": "Your request parameters didn't validate",
|
||||||
|
"detail": "Optional details about the error.",
|
||||||
|
"status": iris.StatusBadRequest,
|
||||||
|
"customField1": customValue1,
|
||||||
|
"customField2": customValue2,
|
||||||
|
})
|
||||||
|
|
||||||
|
// OR
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Response like JSON but with indent of " " and
|
||||||
|
// content type of "application/problem+json"
|
||||||
|
ctx.Problem(newProductProblem("product name", "problem error details"))
|
||||||
|
}
|
||||||
|
|
|
@ -385,6 +385,8 @@ type Context interface {
|
||||||
// Look `StatusCode` too.
|
// Look `StatusCode` too.
|
||||||
GetStatusCode() int
|
GetStatusCode() int
|
||||||
|
|
||||||
|
// AbsoluteURI parses the "s" and returns its absolute URI form.
|
||||||
|
AbsoluteURI(s string) string
|
||||||
// Redirect sends a redirect response to the client
|
// Redirect sends a redirect response to the client
|
||||||
// to a specific url or relative path.
|
// to a specific url or relative path.
|
||||||
// accepts 2 parameters string and an optional int
|
// accepts 2 parameters string and an optional int
|
||||||
|
@ -395,7 +397,6 @@ type Context interface {
|
||||||
// or 303 (StatusSeeOther) if POST method,
|
// or 303 (StatusSeeOther) if POST method,
|
||||||
// or StatusTemporaryRedirect(307) if that's nessecery.
|
// or StatusTemporaryRedirect(307) if that's nessecery.
|
||||||
Redirect(urlToRedirect string, statusHeader ...int)
|
Redirect(urlToRedirect string, statusHeader ...int)
|
||||||
|
|
||||||
// +------------------------------------------------------------+
|
// +------------------------------------------------------------+
|
||||||
// | Various Request and Post Data |
|
// | Various Request and Post Data |
|
||||||
// +------------------------------------------------------------+
|
// +------------------------------------------------------------+
|
||||||
|
@ -777,6 +778,14 @@ type Context interface {
|
||||||
HTML(format string, args ...interface{}) (int, error)
|
HTML(format string, args ...interface{}) (int, error)
|
||||||
// JSON marshals the given interface object and writes the JSON response.
|
// JSON marshals the given interface object and writes the JSON response.
|
||||||
JSON(v interface{}, options ...JSON) (int, error)
|
JSON(v interface{}, options ...JSON) (int, error)
|
||||||
|
// Problem writes a JSON problem response.
|
||||||
|
// Order of fields are not always the same.
|
||||||
|
//
|
||||||
|
// Behaves exactly like `Context.JSON`
|
||||||
|
// but with indent of " " and a content type of "application/problem+json" instead.
|
||||||
|
//
|
||||||
|
// Read more at: https://github.com/kataras/iris/wiki/Routing-error-handlers
|
||||||
|
Problem(v interface{}, opts ...JSON) (int, error)
|
||||||
// JSONP marshals the given interface object and writes the JSON response.
|
// JSONP marshals the given interface object and writes the JSON response.
|
||||||
JSONP(v interface{}, options ...JSONP) (int, error)
|
JSONP(v interface{}, options ...JSONP) (int, error)
|
||||||
// XML marshals the given interface object and writes the XML response.
|
// XML marshals the given interface object and writes the XML response.
|
||||||
|
@ -1612,19 +1621,7 @@ func (ctx *context) IsWWW() bool {
|
||||||
// FullRqeuestURI returns the full URI,
|
// FullRqeuestURI returns the full URI,
|
||||||
// including the scheme, the host and the relative requested path/resource.
|
// including the scheme, the host and the relative requested path/resource.
|
||||||
func (ctx *context) FullRequestURI() string {
|
func (ctx *context) FullRequestURI() string {
|
||||||
scheme := ctx.request.URL.Scheme
|
return ctx.AbsoluteURI(ctx.Path())
|
||||||
if scheme == "" {
|
|
||||||
if ctx.request.TLS != nil {
|
|
||||||
scheme = "https:"
|
|
||||||
} else {
|
|
||||||
scheme = "http:"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
host := ctx.Host()
|
|
||||||
path := ctx.Path()
|
|
||||||
|
|
||||||
return scheme + "//" + host + path
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const xForwardedForHeaderKey = "X-Forwarded-For"
|
const xForwardedForHeaderKey = "X-Forwarded-For"
|
||||||
|
@ -2367,6 +2364,59 @@ func uploadTo(fh *multipart.FileHeader, destDirectory string) (int64, error) {
|
||||||
return io.Copy(out, src)
|
return io.Copy(out, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AbsoluteURI parses the "s" and returns its absolute URI form.
|
||||||
|
func (ctx *context) AbsoluteURI(s string) string {
|
||||||
|
if s == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if s[0] == '/' {
|
||||||
|
scheme := ctx.request.URL.Scheme
|
||||||
|
if scheme == "" {
|
||||||
|
if ctx.request.TLS != nil {
|
||||||
|
scheme = "https:"
|
||||||
|
} else {
|
||||||
|
scheme = "http:"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
host := ctx.Host()
|
||||||
|
|
||||||
|
return scheme + "//" + host + path.Clean(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
if u, err := url.Parse(s); err == nil {
|
||||||
|
r := ctx.request
|
||||||
|
|
||||||
|
if u.Scheme == "" && u.Host == "" {
|
||||||
|
oldpath := r.URL.Path
|
||||||
|
if oldpath == "" {
|
||||||
|
oldpath = "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
if s == "" || s[0] != '/' {
|
||||||
|
olddir, _ := path.Split(oldpath)
|
||||||
|
s = olddir + s
|
||||||
|
}
|
||||||
|
|
||||||
|
var query string
|
||||||
|
if i := strings.Index(s, "?"); i != -1 {
|
||||||
|
s, query = s[:i], s[i:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up but preserve trailing slash
|
||||||
|
trailing := strings.HasSuffix(s, "/")
|
||||||
|
s = path.Clean(s)
|
||||||
|
if trailing && !strings.HasSuffix(s, "/") {
|
||||||
|
s += "/"
|
||||||
|
}
|
||||||
|
s += query
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// Redirect sends a redirect response to the client
|
// Redirect sends a redirect response to the client
|
||||||
// to a specific url or relative path.
|
// to a specific url or relative path.
|
||||||
// accepts 2 parameters string and an optional int
|
// accepts 2 parameters string and an optional int
|
||||||
|
@ -2964,6 +3014,9 @@ const (
|
||||||
ContentHTMLHeaderValue = "text/html"
|
ContentHTMLHeaderValue = "text/html"
|
||||||
// ContentJSONHeaderValue header value for JSON data.
|
// ContentJSONHeaderValue header value for JSON data.
|
||||||
ContentJSONHeaderValue = "application/json"
|
ContentJSONHeaderValue = "application/json"
|
||||||
|
// ContentJSONProblemHeaderValue header value for API problem error.
|
||||||
|
// Read more at: https://tools.ietf.org/html/rfc7807
|
||||||
|
ContentJSONProblemHeaderValue = "application/problem+json"
|
||||||
// ContentJavascriptHeaderValue header value for JSONP & Javascript data.
|
// ContentJavascriptHeaderValue header value for JSONP & Javascript data.
|
||||||
ContentJavascriptHeaderValue = "application/javascript"
|
ContentJavascriptHeaderValue = "application/javascript"
|
||||||
// ContentTextHeaderValue header value for Text data.
|
// ContentTextHeaderValue header value for Text data.
|
||||||
|
@ -3133,6 +3186,30 @@ func (ctx *context) JSON(v interface{}, opts ...JSON) (n int, err error) {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Problem writes a JSON problem response.
|
||||||
|
// Order of fields are not always the same.
|
||||||
|
//
|
||||||
|
// Behaves exactly like `Context.JSON`
|
||||||
|
// but with indent of " " and a content type of "application/problem+json" instead.
|
||||||
|
//
|
||||||
|
// Read more at: https://github.com/kataras/iris/wiki/Routing-error-handlers
|
||||||
|
func (ctx *context) Problem(v interface{}, opts ...JSON) (int, error) {
|
||||||
|
options := DefaultJSONOptions
|
||||||
|
if len(opts) > 0 {
|
||||||
|
options = opts[0]
|
||||||
|
} else {
|
||||||
|
options.Indent = " "
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.contentTypeOnce(ContentJSONProblemHeaderValue, "")
|
||||||
|
|
||||||
|
if p, ok := v.(Problem); ok {
|
||||||
|
p.updateTypeToAbsolute(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.JSON(v, options)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
finishCallbackB = []byte(");")
|
finishCallbackB = []byte(");")
|
||||||
)
|
)
|
||||||
|
@ -3333,10 +3410,11 @@ type N struct {
|
||||||
Markdown []byte
|
Markdown []byte
|
||||||
Binary []byte
|
Binary []byte
|
||||||
|
|
||||||
JSON interface{}
|
JSON interface{}
|
||||||
JSONP interface{}
|
Problem Problem
|
||||||
XML interface{}
|
JSONP interface{}
|
||||||
YAML interface{}
|
XML interface{}
|
||||||
|
YAML interface{}
|
||||||
|
|
||||||
Other []byte // custom content types.
|
Other []byte // custom content types.
|
||||||
}
|
}
|
||||||
|
@ -3354,6 +3432,8 @@ func (n N) SelectContent(mime string) interface{} {
|
||||||
return n.Binary
|
return n.Binary
|
||||||
case ContentJSONHeaderValue:
|
case ContentJSONHeaderValue:
|
||||||
return n.JSON
|
return n.JSON
|
||||||
|
case ContentJSONProblemHeaderValue:
|
||||||
|
return n.Problem
|
||||||
case ContentJavascriptHeaderValue:
|
case ContentJavascriptHeaderValue:
|
||||||
return n.JSONP
|
return n.JSONP
|
||||||
case ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue:
|
case ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue:
|
||||||
|
@ -3485,6 +3565,8 @@ func (ctx *context) Negotiate(v interface{}) (int, error) {
|
||||||
return ctx.Markdown(v.([]byte))
|
return ctx.Markdown(v.([]byte))
|
||||||
case ContentJSONHeaderValue:
|
case ContentJSONHeaderValue:
|
||||||
return ctx.JSON(v)
|
return ctx.JSON(v)
|
||||||
|
case ContentJSONProblemHeaderValue:
|
||||||
|
return ctx.Problem(v)
|
||||||
case ContentJavascriptHeaderValue:
|
case ContentJavascriptHeaderValue:
|
||||||
return ctx.JSONP(v)
|
return ctx.JSONP(v)
|
||||||
case ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue:
|
case ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue:
|
||||||
|
@ -3614,6 +3696,19 @@ func (n *NegotiationBuilder) JSON(v ...interface{}) *NegotiationBuilder {
|
||||||
return n.MIME(ContentJSONHeaderValue, content)
|
return n.MIME(ContentJSONHeaderValue, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Problem registers the "application/problem+json" content type and, optionally,
|
||||||
|
// a value that `Context.Negotiate` will render
|
||||||
|
// when a client accepts the "application/problem+json" content type.
|
||||||
|
//
|
||||||
|
// Returns itself for recursive calls.
|
||||||
|
func (n *NegotiationBuilder) Problem(v ...interface{}) *NegotiationBuilder {
|
||||||
|
var content interface{}
|
||||||
|
if len(v) > 0 {
|
||||||
|
content = v[0]
|
||||||
|
}
|
||||||
|
return n.MIME(ContentJSONProblemHeaderValue, content)
|
||||||
|
}
|
||||||
|
|
||||||
// JSONP registers the "application/javascript" content type and, optionally,
|
// JSONP registers the "application/javascript" content type and, optionally,
|
||||||
// a value that `Context.Negotiate` will render
|
// a value that `Context.Negotiate` will render
|
||||||
// when a client accepts the "application/javascript" content type.
|
// when a client accepts the "application/javascript" content type.
|
||||||
|
@ -3867,6 +3962,12 @@ func (n *NegotiationAcceptBuilder) JSON() *NegotiationAcceptBuilder {
|
||||||
return n.MIME(ContentJSONHeaderValue)
|
return n.MIME(ContentJSONHeaderValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Problem adds the "application/problem+json" as accepted client content type.
|
||||||
|
// Returns itself.
|
||||||
|
func (n *NegotiationAcceptBuilder) Problem() *NegotiationAcceptBuilder {
|
||||||
|
return n.MIME(ContentJSONProblemHeaderValue)
|
||||||
|
}
|
||||||
|
|
||||||
// JSONP adds the "application/javascript" as accepted client content type.
|
// JSONP adds the "application/javascript" as accepted client content type.
|
||||||
// Returns itself.
|
// Returns itself.
|
||||||
func (n *NegotiationAcceptBuilder) JSONP() *NegotiationAcceptBuilder {
|
func (n *NegotiationAcceptBuilder) JSONP() *NegotiationAcceptBuilder {
|
||||||
|
|
172
context/problem.go
Normal file
172
context/problem.go
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Problem Details for HTTP APIs.
|
||||||
|
// Pass a Problem value to `context.Problem` to
|
||||||
|
// write an "application/problem+json" response.
|
||||||
|
//
|
||||||
|
// Read more at: https://github.com/kataras/iris/wiki/Routing-error-handlers
|
||||||
|
type Problem map[string]interface{}
|
||||||
|
|
||||||
|
// NewProblem retruns a new Problem.
|
||||||
|
// Head over to the `Problem` type godoc for more.
|
||||||
|
func NewProblem() Problem {
|
||||||
|
p := make(Problem)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Problem) keyExists(key string) bool {
|
||||||
|
if p == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
_, found := p[key]
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultProblemStatusCode is being sent to the client
|
||||||
|
// when Problem's status is not a valid one.
|
||||||
|
var DefaultProblemStatusCode = http.StatusBadRequest
|
||||||
|
|
||||||
|
func (p Problem) getStatus() (int, bool) {
|
||||||
|
statusField, found := p["status"]
|
||||||
|
if !found {
|
||||||
|
return DefaultProblemStatusCode, false
|
||||||
|
}
|
||||||
|
|
||||||
|
status, ok := statusField.(int)
|
||||||
|
if !ok {
|
||||||
|
return DefaultProblemStatusCode, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !StatusCodeNotSuccessful(status) {
|
||||||
|
return DefaultProblemStatusCode, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return status, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEmptyTypeURI(uri string) bool {
|
||||||
|
return uri == "" || uri == "about:blank"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Problem) getType() string {
|
||||||
|
typeField, found := p["type"]
|
||||||
|
if found {
|
||||||
|
if typ, ok := typeField.(string); ok {
|
||||||
|
if !isEmptyTypeURI(typ) {
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates "type" field to absolute URI, recursively.
|
||||||
|
func (p Problem) updateTypeToAbsolute(ctx Context) {
|
||||||
|
if p == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if uriRef := p.getType(); uriRef != "" {
|
||||||
|
p.Type(ctx.AbsoluteURI(uriRef))
|
||||||
|
}
|
||||||
|
|
||||||
|
if cause, ok := p["cause"]; ok {
|
||||||
|
if causeP, ok := cause.(Problem); ok {
|
||||||
|
causeP.updateTypeToAbsolute(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key sets a custom key-value pair.
|
||||||
|
func (p Problem) Key(key string, value interface{}) Problem {
|
||||||
|
p[key] = value
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type URI SHOULD resolve to HTML [W3C.REC-html5-20141028]
|
||||||
|
// documentation that explains how to resolve the problem.
|
||||||
|
// Example: "https://example.net/validation-error"
|
||||||
|
//
|
||||||
|
// Empty URI or "about:blank", when used as a problem type,
|
||||||
|
// indicates that the problem has no additional semantics beyond that of the HTTP status code.
|
||||||
|
// When "about:blank" is used,
|
||||||
|
// the title is being automatically set the same as the recommended HTTP status phrase for that code
|
||||||
|
// (e.g., "Not Found" for 404, and so on) on `Status` call.
|
||||||
|
//
|
||||||
|
// Relative paths are also valid when writing this Problem to an Iris Context.
|
||||||
|
func (p Problem) Type(uri string) Problem {
|
||||||
|
return p.Key("type", uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Title sets the problem's title field.
|
||||||
|
// Example: "Your request parameters didn't validate."
|
||||||
|
// It is set to status Code text if missing,
|
||||||
|
// (e.g., "Not Found" for 404, and so on).
|
||||||
|
func (p Problem) Title(title string) Problem {
|
||||||
|
return p.Key("title", title)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status sets HTTP error code for problem's status field.
|
||||||
|
// Example: 404
|
||||||
|
//
|
||||||
|
// It is required.
|
||||||
|
func (p Problem) Status(statusCode int) Problem {
|
||||||
|
shouldOverrideTitle := !p.keyExists("title")
|
||||||
|
|
||||||
|
if !shouldOverrideTitle {
|
||||||
|
typ, found := p["type"]
|
||||||
|
shouldOverrideTitle = !found || isEmptyTypeURI(typ.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if shouldOverrideTitle {
|
||||||
|
// Set title by code.
|
||||||
|
p.Title(http.StatusText(statusCode))
|
||||||
|
}
|
||||||
|
return p.Key("status", statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail sets the problem's detail field.
|
||||||
|
// Example: "Optional details about the error...".
|
||||||
|
func (p Problem) Detail(detail string) Problem {
|
||||||
|
return p.Key("detail", detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cause sets the problem's cause field.
|
||||||
|
// Any chain of problems.
|
||||||
|
func (p Problem) Cause(cause Problem) Problem {
|
||||||
|
if !cause.Validate() {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.Key("cause", cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate reports whether this Problem value is a valid problem one.
|
||||||
|
func (p Problem) Validate() bool {
|
||||||
|
// A nil problem is not a valid one.
|
||||||
|
if p == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.keyExists("type") &&
|
||||||
|
p.keyExists("title") &&
|
||||||
|
p.keyExists("status")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error method completes the go error.
|
||||||
|
// Returns the "[Status] Title" string form of this Problem.
|
||||||
|
// If Problem is not a valid one, it returns "invalid problem".
|
||||||
|
func (p Problem) Error() string {
|
||||||
|
if !p.Validate() {
|
||||||
|
return "invalid problem"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("[%d] %s", p["status"], p["title"])
|
||||||
|
}
|
2
doc.go
2
doc.go
|
@ -38,7 +38,7 @@ Source code and other details for the project are available at GitHub:
|
||||||
|
|
||||||
Current Version
|
Current Version
|
||||||
|
|
||||||
11.2.4
|
11.2.5
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
|
|
||||||
|
|
10
go19.go
10
go19.go
|
@ -48,8 +48,16 @@ type (
|
||||||
// See `NewConditionalHandler` for more.
|
// See `NewConditionalHandler` for more.
|
||||||
// An alias for the `context/Filter`.
|
// An alias for the `context/Filter`.
|
||||||
Filter = context.Filter
|
Filter = context.Filter
|
||||||
// A Map is a shortcut of the map[string]interface{}.
|
// A Map is an alias of map[string]interface{}.
|
||||||
Map = context.Map
|
Map = context.Map
|
||||||
|
// Problem Details for HTTP APIs.
|
||||||
|
// Pass a Problem value to `context.Problem` to
|
||||||
|
// write an "application/problem+json" response.
|
||||||
|
//
|
||||||
|
// Read more at: https://github.com/kataras/iris/wiki/Routing-error-handlers
|
||||||
|
//
|
||||||
|
// It is an alias of `context.Problem` type.
|
||||||
|
Problem = context.Problem
|
||||||
|
|
||||||
// Supervisor is a shortcut of the `host#Supervisor`.
|
// Supervisor is a shortcut of the `host#Supervisor`.
|
||||||
// Used to add supervisor configurators on common Runners
|
// Used to add supervisor configurators on common Runners
|
||||||
|
|
8
iris.go
8
iris.go
|
@ -37,7 +37,7 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Version is the current version number of the Iris Web Framework.
|
// Version is the current version number of the Iris Web Framework.
|
||||||
Version = "11.2.4"
|
Version = "11.2.5"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTP status codes as registered with IANA.
|
// HTTP status codes as registered with IANA.
|
||||||
|
@ -453,7 +453,6 @@ var (
|
||||||
//
|
//
|
||||||
// A shortcut of the `cache#Cache304`.
|
// A shortcut of the `cache#Cache304`.
|
||||||
Cache304 = cache.Cache304
|
Cache304 = cache.Cache304
|
||||||
|
|
||||||
// CookiePath is a `CookieOption`.
|
// CookiePath is a `CookieOption`.
|
||||||
// Use it to change the cookie's Path field.
|
// Use it to change the cookie's Path field.
|
||||||
//
|
//
|
||||||
|
@ -499,6 +498,11 @@ var (
|
||||||
//
|
//
|
||||||
// A shortcut for the `context#IsErrPath`.
|
// A shortcut for the `context#IsErrPath`.
|
||||||
IsErrPath = context.IsErrPath
|
IsErrPath = context.IsErrPath
|
||||||
|
// NewProblem retruns a new Problem.
|
||||||
|
// Head over to the `Problem` type godoc for more.
|
||||||
|
//
|
||||||
|
// A shortcut for the `context#NewProblem`.
|
||||||
|
NewProblem = context.NewProblem
|
||||||
)
|
)
|
||||||
|
|
||||||
// Contains the enum values of the `Context.GetReferrer()` method,
|
// Contains the enum values of the `Context.GetReferrer()` method,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user