Access the interactive backlash/Werkzeug debugger in a failed AJAX request against a TurboGears server

1.8k views Asked by At

TurboGears features backlash, a great interactive debugger in the browser, based on the Werkzeug Debugger. When debugging is turned on in the server configuration, if a request fails, the server responds with an interactive Web page where you can watch a Python traceback that can be inspected interactively.

However, when developing client-side applications in jQuery or AngularJS, how can I get access to the interactive debugger when an AJAX request fails?

2

There are 2 answers

2
Martin Thorsen Ranang On BEST ANSWER

When your AJAX requests fail on the server, you can replace your current document contents with the debug/error document from the servers response. For example, you can do something like the following:

$.ajax({
    url: 'failing_controller/',
    type: 'POST'
})
    .fail(function _handleFailure(jqXHR, textStatus, errorThrown) {
        document.open();
        document.write(jqXHR.responseText);
        document.close();
    })
    .success(function _handleSuccess(data, textStatus, jqXHR) {
        // ... handle data ...
    });

You will probably want to replace the failure handler with something more proper in a production environment.

0
Ryan On

My Flask application is running locally through Google App Engine's development server. I have the server configured to serve static files from outside the Flask app's context, but Werkzeug Debugger's script-file requests need to go through the Flask app. I have adapted mtr's answer to my situation:

// ...
.fail(function (jqXHR) {
  // open debugger in new window
  var endpointUrl = 'endpoint-url-of-your-ajax-request-without-query-params',
      debuggerWindow = window.open('', 'werkzeug debugger');
  debuggerWindow.document.open();
  debuggerWindow.location.href = endpointUrl;
  // set href after document.open() instead of before because
  // document.open() erases the path from the new window location's href
  debuggerWindow.document.write(jqXHR.responseText);
  // now script requests in the HTML (i.e., in jqXHR.responseText) will
  // be made relative to endpointUrl rather than relative to the root path
  // and hence will go through the Flask app
  debuggerWindow.document.close();
})
// ...

UPDATE

The code above works only for GET requests and only incidentally. debuggerWindow.location.href = endpointUrl causes the window to submit a GET request to endpointUrl. So you end up with the stack trace from a second GET request to endpointUrl.

To open the original traceback for any type of request, I am using the following implementation (in ES6):

/*
thanks to:
    http://stackoverflow.com/a/3354511/1941513
    http://ilee.co.uk/changing-url-without-page-refresh/
    http://stackoverflow.com/a/4001415/1941513
    http://stackoverflow.com/a/11933007/1941513
    http://stackoverflow.com/a/3340186/1941513
*/
const werkzeugDebugger = (flaskResponse, url) => {
    if (!sameOrigin(url)) return;

    if(!confirm('__DEV__: Server Error!  Open Werkzeug Debugger?')) return;

    window.history.pushState({}, 'Werkzeug Debugger', url);

    try {
        window.document.open();
        window.document.write(flaskResponse);
    }
    finally {
        window.document.close();
    }
};


/*
thanks to: https://gist.github.com/jlong/2428561
*/
const sameOrigin = url => {
    const parser = document.createElement('a');
    parser.href = url;
    return !parser.origin || parser.origin === window.location.origin;
};

When in dev mode, my code checks whether a response has a 500-level HTTP status. If so, it extracts the text of the response body (response.responseText for jQuery or response.text() for fetch) and passes it and the URL in a call to werkzeugDebugger.