Skip to content
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

Change error responses from plain text to JSON #4845

Merged
merged 4 commits into from
Dec 2, 2021

Conversation

chaudum
Copy link
Contributor

@chaudum chaudum commented Nov 30, 2021

Signed-off-by: Christian Haudum christian.haudum@gmail.com

What this PR does / why we need it:

Set correct content-type header when returning error responses with plain text body.

Which issue(s) this PR fixes:
Fixes #4844

Checklist

  • Documentation added
  • Tests updated
  • Add an entry in the CHANGELOG.md about the changes.

@chaudum chaudum force-pushed the chaudum/error-content-type branch 2 times, most recently from 6e99e83 to da5fd0d Compare November 30, 2021 10:09
@chaudum chaudum changed the title Set correct content type header when returning 4xx errors Set correct content type header when returning plain text error response Nov 30, 2021
@chaudum chaudum marked this pull request as ready for review November 30, 2021 10:50
@chaudum chaudum requested a review from a team as a code owner November 30, 2021 10:50
@@ -236,6 +236,7 @@ func writeError(w http.ResponseWriter, err error) {
err = errRequestEntityTooLarge
}
}
w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hhmm, I wonder if it's better for us to instead format the error as JSON so that at least we have some consistency.
What made you take this approach vs that one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, good point, I've also considered that, and thinking about it again, it's clearly the more consistent approach. I guess, people do not really depend on the response body being kept the same.
Also, I think there are still other occurrences where the response body is plain text. Will update the PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't that break the API? I'm not sure how strict we are there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few thoughts:

  • the API is inconsistent
  • if we're changing anything it's technically breaking the API
  • I'd rather break it in the direction of consistency 😝

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with consistency even by breaking the API response. But should definitely add it in our docs/sources/upgrading/_index.md. (/cc @chaudum )

@chaudum chaudum marked this pull request as draft November 30, 2021 16:33

func JSONError(w http.ResponseWriter, code int, message string, args ...interface{}) {
w.WriteHeader(code)
w.Header().Set("Content-Type", "application/json; charset=UTF8")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
w.Header().Set("Content-Type", "application/json; charset=UTF8")
w.Header().Set("Content-Type", "application/json; charset=UTF-8")

pkg/util/server/error_test.go Show resolved Hide resolved
@pull-request-size pull-request-size bot added size/L and removed size/M labels Dec 1, 2021
@chaudum chaudum changed the title Set correct content type header when returning plain text error response Change error responses from plain text to JSON Dec 1, 2021
The API returned errors in plain text where the error message was the
response body. However, the content type header was set to
`application/json`.

To make the API responses consistent, errors are now returned as JSON as
well.

```
{
  "message": string,
  "status": string,
  "code": int
}
```

Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
@chaudum chaudum marked this pull request as ready for review December 1, 2021 15:47
Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
Copy link
Contributor

@KMiller-Grafana KMiller-Grafana left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs portion of the PR looks good to me.

Copy link
Contributor

@dannykopping dannykopping left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partial review; have a couple questions

}
```

"404 Not Found" errors still return responses with content type `text/plain`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we special-case 404s? This is going to make writing Loki clients complex.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

404 errors are handled differently than errors that occur in our handlers. However, it should be possible to override the NotFoundHandler of the router.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you look into that please? We should really try have consistency across the board if possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was actually pretty easy to achieve: 4820322

@@ -39,21 +39,21 @@ func (dm *DeleteRequestHandler) AddDeleteRequestHandler(w http.ResponseWriter, r
ctx := r.Context()
userID, err := tenant.TenantID(ctx)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
serverutil.JSONError(w, http.StatusBadRequest, err.Error())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not too familiar with how HTTP errors are handled, but are they not all handled pkg/util/server/error.go -> WriteError? If we use http.Error here does it kill the request and write this error directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WriteError handles generic errors as Internal Server Error. Therefore we bypass WriteError and call JSONError directly, so we can specify a status code and an error message.

http.Error sets the status code, content type header (text/plain) and writes the response body. Usually you want to return thereafter.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK thanks for that. I was looking for a way that we could somehow add middleware to catch these http.Error calls, but they write directly to the http.ResponseWriter :(

I would've loved to have stuck with idiomatic gorilla/mux & net/http, but it seems we don't have a good layer of indirection here.

What you've got here looks like a good solution 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An option to catch and resolve errors in a middleware would be, to write an occurring error to the request context and check for it in the middleware handler. I don't think that is very idiomatic, though. Writing the error to the response writer seems to be the common way.

Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
Copy link
Contributor

@dannykopping dannykopping left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍 great work @chaudum
One tiny nit then I'll merge

@@ -34,9 +34,13 @@ type ErrorResponseBody struct {
Message string `json:"message"`
}

func NotFoundHandler(w http.ResponseWriter, r *http.Request) {
JSONError(w, 404, "page not found")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<nit>

Suggested change
JSONError(w, 404, "page not found")
JSONError(w, 404, "not found")

</nit>

We don't really work with "pages" - let's not confuse the operator

Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
@dannykopping dannykopping merged commit e573a4d into main Dec 2, 2021
@dannykopping dannykopping deleted the chaudum/error-content-type branch December 2, 2021 14:52
@chaudum
Copy link
Contributor Author

chaudum commented Dec 3, 2021

@darrenjaneczek Could this potentially affect the Loki datasource plugin / GEL plugin?

slim-bean added a commit that referenced this pull request Apr 5, 2022
…nt type from plain text to JSON is a breaking change of the API and we should wait to do this at a major release.

Signed-off-by: Ed Welch <edward.welch@grafana.com>
slim-bean added a commit that referenced this pull request Apr 5, 2022
…5772)

* This reverts the changes from #4845, unfortunately changing the content type from plain text to JSON is a breaking change of the API and we should wait to do this at a major release.

Signed-off-by: Ed Welch <edward.welch@grafana.com>

* fix tests which were updated after initial change.

Signed-off-by: Ed Welch <edward.welch@grafana.com>
grafanabot pushed a commit that referenced this pull request Apr 5, 2022
…5772)

* This reverts the changes from #4845, unfortunately changing the content type from plain text to JSON is a breaking change of the API and we should wait to do this at a major release.

Signed-off-by: Ed Welch <edward.welch@grafana.com>

* fix tests which were updated after initial change.

Signed-off-by: Ed Welch <edward.welch@grafana.com>
(cherry picked from commit c158b2c)
slim-bean added a commit that referenced this pull request Apr 6, 2022
…5772) (#5774)

* This reverts the changes from #4845, unfortunately changing the content type from plain text to JSON is a breaking change of the API and we should wait to do this at a major release.

Signed-off-by: Ed Welch <edward.welch@grafana.com>

* fix tests which were updated after initial change.

Signed-off-by: Ed Welch <edward.welch@grafana.com>
(cherry picked from commit c158b2c)

Co-authored-by: Ed Welch <edward.welch@grafana.com>
splitice pushed a commit to X4BNet/loki that referenced this pull request May 21, 2022
… API (grafana#5772) (grafana#5774)

* This reverts the changes from grafana#4845, unfortunately changing the content type from plain text to JSON is a breaking change of the API and we should wait to do this at a major release.

Signed-off-by: Ed Welch <edward.welch@grafana.com>

* fix tests which were updated after initial change.

Signed-off-by: Ed Welch <edward.welch@grafana.com>
(cherry picked from commit c158b2c)

Co-authored-by: Ed Welch <edward.welch@grafana.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Wrong content type header for Bad Request response
6 participants