-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[New Feature] Problem Details for HTTP APIs #1335
Comments
Done with: #1336 |
Hi @kataras |
@Dexus I did that on the initial implementation but then I re-written and forgot to send the status code, you are right, it's done now. |
@kataras thanks.
Can you also add this, too? Like: |
I would love, although the java implementation (the most complete we know) missing that as I could see, but don't forget you can always add a PR, your notes are quite helpful! The Problem type is map[string]interface{}, we can't save properties inside there, the
|
Option 1 would be another braking change. Currently only Option 2 would be my favorite, because we can use a key that violates the RFC. Otherwise only alphanumeric and "_" are allowed.
I could imagine myself using a key like "::OPT::" internally. Even if it wouldn't be ideal for performance, because of the special characters.
Thanks, after my holiday I will gladly submit a PR for possible improvements/ideas. |
The first implementation on setting them on the map itself, looks like this: const (
problemTempKeyPrefix = "@temp_"
retryAfterHeaderKey = "Retry-After"
)
// No need, let's remove them on receive, once.
//
// func (p Problem) removeTempProperties() {
// for k, v := range p {
// if strings.HasPrefix(k, problemTempKeyPrefix) {
// delete(p, k)
// continue
// }
//
// if k == "cause" {
// if causeP, ok := v.(Problem); ok {
// causeP.removeTempProperties()
// }
// }
// }
// }
// TempKey sets a temporary key-value pair, which is being removed
// on the its first get.
func (p Problem) TempKey(key string, value interface{}) Problem {
return p.Key(problemTempKeyPrefix+key, value)
}
func (p Problem) getTempKey(key string) interface{} {
key = problemTempKeyPrefix + key
v, ok := p[key]
if ok {
delete(p, key)
return v
}
return nil
}
func parseRetryAfter(value interface{}, timeLayout string) string {
// https://tools.ietf.org/html/rfc7231#section-7.1.3
// Retry-After = HTTP-date / delay-seconds
switch v := value.(type) {
case time.Time:
return v.Format(timeLayout)
case int64:
return strconv.FormatInt(v, 10)
case string:
dur, err := time.ParseDuration(v)
if err != nil {
t, err := time.Parse(timeLayout, v)
if err != nil {
return ""
}
return parseRetryAfter(t, timeLayout)
}
return parseRetryAfter(parseDurationToSeconds(dur), timeLayout)
}
return ""
}
func (p Problem) getRetryAfter(timeLayout string) string {
value := p.getTempKey(retryAfterHeaderKey)
if value == nil {
return ""
}
return parseRetryAfter(value, timeLayout)
}
func parseDurationToSeconds(dur time.Duration) int64 {
return int64(math.Round(dur.Seconds()))
}
// RetryAfter sets a temp key to be set on Retry-After response header.
// https://tools.ietf.org/html/rfc7231#section-7.1.3
// The value can be one of those:
// time.Time
// time.Duration for seconds
// int64, int, float64 for seconds
// string for duration string or for datetime string.
func (p Problem) RetryAfter(dateOrDurationOrIntSecondsOrString interface{}) Problem {
switch v := dateOrDurationOrIntSecondsOrString.(type) {
case time.Time:
return p.TempKey(retryAfterHeaderKey, v)
case time.Duration:
return p.TempKey(retryAfterHeaderKey, parseDurationToSeconds(v))
case int64:
return p.TempKey(retryAfterHeaderKey, v)
case int:
return p.TempKey(retryAfterHeaderKey, int64(v))
case float64:
return p.TempKey(retryAfterHeaderKey, int64(math.Round(v)))
case string:
return p.TempKey(retryAfterHeaderKey, v)
default:
// panic?
}
return p
} And context gets the retry header and set it. However, with this implementation we have an issue of a |
Looks good so far. In the function That the I'm having a hard time to look deeper and understand the code with my mobile phone in hand at the moment. https://github.com/kataras/iris/blob/master/context/problem.go#L80 Think this should also be a loop over the |
Then we will have to clone the Problem, remove this key, and push it as JSON so the caller's Problem don't change, this will have a performance cost (cloning the map).
What do you mean? to check for a valid retry-after to all children or take the maximum or add all of them? That's why I think to make it as Options, currently it accepts JSON options but we can wrap to a new ProblemOptions for things like that, meanwhile the end-dev can set the |
I made it with options, even if we have a 2-days breaking change, the fix is easy just pass the prev JSON on the ProblemOptions.JSON field. It's far better and can be re-used by end-dev and on the future for more options. No default values there except the json indentation to " ". ctx.Problem(newProductProblem("product name", "problem details"), iris.ProblemOptions{
// Optional JSON renderer settings.
JSON: iris.JSON{
Indent: " ",
},
// Can accept:
// time.Time for HTTP-Date,
// time.Duration, int64, float64, int for seconds
// or string for date or duration.
// Examples:
// time.Now().Add(5 * time.Minute),
// 300 * time.Second,
// "5m",
//
RetryAfter: 300,
// A function that, if specified, can dynamically set
// retry-after based on the request. Useful for ProblemOptions reusability.
// Overrides the RetryAfter field.
//
// RetryAfterFunc: func(iris.Context) interface{} { [...]}
})
} The Problem type itself still has |
Otherwise, I agree with you, is fair enough, for 2 days, to use the new options. So it has less impact on performance. |
Yes, I think it's better that way @Dexus. Happy holidays man, you are quite lucky :) |
…t to render and give option to render a Problem as XML - rel to #1335
Former-commit-id: ff789b6d535080c88e05c81ab3fb7d9689801ec7
as remindered at kataras#1335 (comment) Former-commit-id: 7fc7742c5c3374718fbf39530c45fa068b5d433a
Former-commit-id: 6c03439d21175f120c37a7d8dd067a0d10de837a
Former-commit-id: 7bbb330cb4e769222ede5be0adbddc4e37df9e3b
…t to render and give option to render a Problem as XML - rel to kataras#1335 Former-commit-id: dcf21098ff7af6becfa9896df5f82c3b0b53f0ac
Based on:
Problem { type, title, status, detail, cause: Problem{ ... }, $any_custom }
application/problem+json
For the upcoming Iris v11.2.5
The text was updated successfully, but these errors were encountered: