Refactor exception handling to return instead of echoing.

By returning a response/string instead of directly echoing we get a few benefits:

1. We can more easily test the code without relying on ob_start().
2. Application exception handlers can have full control over the response headers.
3. We can fix issues related to nested error pages.

This is a **breaking change** as application exception renderers now **must** return either a string or a response object. I will update the docs once this has been discussed/merged.

Fixes #4130
I mostly allowed strings so updating exception renderers would be a very simple change. I'm not against requiring response objects, but it will mean more extensive changes to the test suite as well.