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
Html exceptions only when accepted #592
Conversation
Rather than be concerned with whether a request is an asynchronous browser request or not it is better to simply consider the Accept header and only serve HTML to clients that specifically ask for it. This way you will not find your pure JSON API application splitting out HTML error messages to your console when using curl :)
* Load HTML exception template only if needed * Only #call is public * Enumerable body concern in one place
end | ||
|
||
def prefers_plain_text?(env) | ||
env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" && (!env["HTTP_ACCEPT"] || !env["HTTP_ACCEPT"].include?("text/html")) | ||
private |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't marking all the following methods as private a too invasive change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be very odd if anyone was making use of these methods so I thought better to close up the public API to make later changes easier.
I would think it very odd if someone was making use of these methods, though I do appreciate your concern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see you point and agree with you, but I think that this kind of changes are not OK unless there is a major version bump to notify of possible breakage.
I've restored the public API to it's previous state, also I've made sure the new method I've added is private. Hope this is now acceptable. I'm happy to squash commits if that's your preference. |
@spastorino @raggi Any thoughts on this? I'm having to monkey patch this change in on all my JSON API apps otherwise every time you get an error it's a huge PITA :) |
@@ -17,7 +17,6 @@ class ShowExceptions | |||
|
|||
def initialize(app) | |||
@app = app | |||
@template = ERB.new(TEMPLATE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ERB parsing is not cheap. Why was this moved, it seems orthogonal to the desired changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a refactoring I thought might be beneficial.
Now no ERB
object is created unless HTML is about to rendered. This should improve performance if anything.
Happy to revert if this is a deal breaker.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You want to improve boot time performance at the sacrifice of runtime performance? I'd be more worried about the extra garbage, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that's right, as the template only needs to rendered in the case of an exception isn't it better to take the performance hit only when one is raised rather than on every request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initialize is not called every request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course you're right. However this change still prevents all template related objects from being created until absolutely necessary.
Looks good in principle. Would you mind addressing the comment? |
As this is orthoganol to HTML rendering change.
While I stand by my refactorings I agree that the discussed changes to the template rendering are orthogonal to the purpose of this PR. I have therefore removed it. |
@@ -85,7 +94,7 @@ def pretty(env, exception) | |||
end | |||
}.compact | |||
|
|||
[@template.result(binding)] | |||
@template.result(binding) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you remove the array here? Doesn't ERB return strings from result?
Rack SPEC says "Body must respond to #each and yield strings". String does not do this in all supported ruby versions in all locales.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've moved the concern of wrapping the response body string in an array to one place. See line 45 https://github.com/bestie/rack/blob/5b2ee1163aa6f68df5ec712b4d9802d90a7875f4/lib/rack/showexceptions.rb#L45
def prefers_plain_text?(env) | ||
env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" && (!env["HTTP_ACCEPT"] || !env["HTTP_ACCEPT"].include?("text/html")) | ||
def accepts_html?(env) | ||
env["HTTP_ACCEPT"] && env["HTTP_ACCEPT"].include?("text/html") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have Rack::Utils.best_q_match
and friends now, that are probably better used here.
Rather than be concerned with whether a request is an asynchronous browser
request or not it is better to simply consider the Accept header and only serve
HTML to clients that specifically ask for it.
This way you will not find your pure JSON API application splitting out HTML
error messages to your console when using curl :)