flask-debugtoolbar icon indicating copy to clipboard operation
flask-debugtoolbar copied to clipboard

Add option to enable the toolbar to display on JSON responses?

Open jeffwidman opened this issue 10 years ago • 9 comments

I am debugging a Flask app that serves up JSON for an API endpoint.

I'm viewing the JSON in the browser, and it'd be convenient to display the toolbar as well.

My understanding of content types is a little hazy... is it possible to add an option to display the toolbar alongside this JSON? Like perhaps a config setting that lets me turn on the toolbar for all responses, not just with <! doctype html>?

jeffwidman avatar Nov 19 '15 08:11 jeffwidman

Jef did you have any luck getting this done?

sumoward avatar Dec 15 '15 11:12 sumoward

No. The way to do it I think, so you don't have to get funky with adding a HTML wrapper around the JSON is detect the content type and have the toolbar append its own bit of JSON with the sqlalchemy queries + page timing info, but thats still a pain methinks compared to just manually setting sqlalchemy to echo queries to stdout and watching your logs as you hit various endpoints.

jeffwidman avatar Dec 15 '15 12:12 jeffwidman

Thanks Jeff.

sumoward avatar Dec 15 '15 12:12 sumoward

Workaround:

@app.after_request
def after_request(response):
    # Wrap JSON responses by HTML and ask flask-debugtoolbar to inject own HTML into it
    if app.config.get("DEBUG_TB_ENABLED", False) and response.mimetype == "application/json" and response.status_code != 401:
        response.direct_passthrough = False  # Work-around for profiling of streamed JSON data download
        args = dict(response=response.data.decode("utf-8"), http_code=response.status)
        html_wrapped_response = make_response(render_template("wrap_json.html", **args), response.status_code)
        response = app.extensions["debugtoolbar"].process_response(html_wrapped_response)

    return response

and

<html>
    <head>
        <title>JSON Response</title>
    </head>

    <body>
        <h1>HTML Wrapper for JSON Response</h1>

        <h2>HTTP Code</h2>
        <pre>{{ http_code }}</pre>

        <h2>JSON Response</h2>
        <pre>{{ response }}</pre>
    </body>
</html>

sarimak avatar Sep 06 '16 14:09 sarimak

I also had to init the FDT like the following to make the above snippet work instead of just DebugToolbarExtension(app)

    app.extensions = getattr(app, 'extensions', {})
    app.extensions['debugtoolbar'] = DebugToolbarExtension(app)

asldevi avatar Oct 24 '17 06:10 asldevi

@sarimak I add code exactly as yours, the toolbar show up on the page, but nothing pop up when click the menus. I compared all the requests to the example in the source, there are two toolbar.css requests send to server, the rest requests are totally same.

guyecode avatar Jan 17 '18 17:01 guyecode

@guyecode You're right, FDT has changed since I posted the workaround. I can reproduce the issue (FDT for JSON response is displayed but ignores click events -- while it works fine for HTML response) with the following code:

import flask
import flask_debugtoolbar

app = flask.Flask('test', template_folder='.')
app.config['DEBUG_TB_ENABLED'] = True
app.config['SECRET_KEY'] = '123'
toolbar = flask_debugtoolbar.DebugToolbarExtension(app)

@app.after_request
def after_request(response):
    if response.mimetype == "application/json":
        html_wrapped_response = flask.make_response(flask.render_template("wrap_json.html", response=response.data.decode("utf-8"), http_code=response.status), response.status_code)
        return toolbar.process_response(html_wrapped_response)

    return response

@app.route('/json')
def test_json():
    return flask.jsonify(dict(key1='value1')), 200

@app.route('/html')
def test_html():
    return '<head/><body>test</body>', 200

if __name__ == '__main__':
    app.run(debug=True)

Unfortunately, I am not skilled enough in JavaScript to troubleshoot the issue.

sarimak avatar Feb 04 '18 10:02 sarimak

@sarimak

modify wrap_json.html to:

<html>
    <head>
        <title>JSON Response</title>
    </head>

    <body>
        <h1>HTML Wrapper for JSON Response</h1>

        <h2>HTTP Code</h2>
        <pre>{{ http_code }}</pre>

        <h2>JSON Response</h2>
        <pre>{{ response }}</pre>
    </body>

<script>
$('#flDebugPanelList').find('li a').click(function () {
    const current = $('#flDebug #' + this.className + '-content');
    if (current.is(':visible')) {
      $(document).trigger('close.flDebug');
      $(this).parent().removeClass('active');
    } else {
      $('.panelContent').hide();
      current.show();

      $('#flDebugToolbar').find('li').removeClass('active');
      $(this).parent().addClass('active');
    }
  });
</script>
</html>

image

RealLiuSha avatar Feb 26 '18 07:02 RealLiuSha

I was also having issues with the click events not working with If you create the after_request before the toolbar extension it will see the html mimetype and work without needing future changes. Here's an extension I wrote for this in my app.

"""Flask Extensions"""
import flask
import flask_debugtoolbar

class DevToolbar:
    """Add debug toolbars with json to html

    Note you must pass `_debug` param to convert the json response
    """
    def __init__(self, app):
        wrap_json = """
        <html>
            <head>
                <title>Debugging JSON Response</title>
            </head>

            <body>
                <h1>Wrapped JSON Response</h1>

                <h2>HTTP Code</h2>
                <pre>{{ http_code }}</pre>

                <h2>JSON Response</h2>
                <pre>{{ response }}</pre>
            </body>
        </html>
        """

        @app.after_request
        def after_request(response):
            if response.mimetype == "application/json" and \
                '_debug' in flask.request.args:
                html_wrapped_response = flask.make_response(
                    flask.render_template_string(
                        wrap_json,
                        response=response.data.decode("utf-8"),
                        http_code=response.status
                    ),
                    response.status_code,
                )
                return app.process_response(html_wrapped_response)

            return response

        flask_debugtoolbar.DebugToolbarExtension(app)

KyleJamesWalker avatar Dec 06 '18 19:12 KyleJamesWalker