How to display the symfony profiler for API request made in the browser?
Asked Answered
B

5

37

I'm developing a REST api with Symfony2 + FOSRest bundle.

I would like to know if there is any way for a call to the api in dev mode (app_dev.php) from the browser (corresponding to a Accept: text/html,application/xhtml+xml header) to display the response in the "specified format", wrapped in html with the profiler provided by symfony.

It would allow to debug calls to the api directly in the browser.


Edit: I don't want to debug the HTTP request but the whole process (route matching, DB queries involved, etc). That's why I want to have access to the symfony profiler.

Bedwarmer answered 4/2, 2014 at 14:52 Comment(6)
in chrome / console, right mousebutton switch on XMLHttprequestsDisvalue
Dev HTTP Client for Chrome, I choose you!Richy
Thanks for your answers but I don't want to debug the HTTP request, I want to have access to the symfony debug toolbar, for debugging the whole process. I have edited my question to clarify that.Bedwarmer
How about loading your API call into an iframe and modifying the code in this response so the toolbar loads in the main frame? The iframe would transform the output in HTML though. Would that be a good solution? If yes, I can write an answer.Kristoferkristoffer
Good point. I didn't think to use the debug token via another request.I think this could even be proposed as a PR to the FOSRest repo. What could be interesting is to have a listener which check if the call come from a browser, and make the framework return data formatted, wrapped in html + sf profiler. But, an iframe should get the job too.Bedwarmer
You should consider changing your accepted answer since there's now an official way of doing this and it's the one mentioned by @SimonSimCityRenfrew
B
55

Since Symfony 2.4, the profiler sets two additional settings in the HTTP header: X-Debug-Token and X-Debug-Token-Link. (see http://symfony.com/blog/new-in-symfony-2-4-quicker-access-to-the-profiler-when-working-on-an-api)

These headers contain the token and the direct link to the profiler for the current request. They are always sent if the profiler is enabled.

Not surprisingly, there is already an extension available for Chrome, that checks for the existence of these headers and provide extra information: Symfony2 Profiler shortcut

In my opinion, this is better than any custom html-wrapper, but this only works for GET and maybe POST requests - PUT and DELETE requests are a bit trickier. There you can use a http client, like the chrome-extension POSTMAN and open the profiler manually by opening the link provided in the http-header X-Debug-Token-Link or keep your profiler-page (f.e. http://example.org/_profiler/) opened.

Burns answered 11/3, 2014 at 8:20 Comment(0)
C
14

The reason the WebDebugToolbar isn't displayed when developing a JSON or XML API is that the toolbar is set to only be injected into HTML-type responses.

To overcome this you could add a kernel.response Event Listener in your Bundle which converts your JSON or XML responses into HTML.

namespace Acme\APIBundle\Event\Listener;

use Symfony\Component\HttpKernel\Event\FilterResponseEvent;

class ConvertToHtmlResponse {
  public function onKernelResponse(FilterResponseEvent $event) {
    if (!$event->isMasterRequest()) {
      return;
    }

    $request = $event->getRequest();

    // Only send back HTML if the requestor allows it
    if (!$request->headers->has('Accept') || (false === strpos($request->headers->get('Accept'), 'text/html'))) {
      return;
    }

    $response = $event->getResponse();
    switch ($request->getRequestFormat()) {
      case 'json':
        $prettyprint_lang = 'js';
        $content = json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT);
        break;

      case 'xml':
        $prettyprint_lang = 'xml';
        $content = $response->getContent();
        break;

      default:
        return;
    }

    $response->setContent(
      '<html><body>' .
      '<pre class="prettyprint lang-' . $prettyprint_lang . '">' .
      htmlspecialchars($content) .
      '</pre>' .
      '<script src="https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/run_prettify.min.js"></script>' .
      '</body></html>'
    );

    // Set the request type to HTML
    $response->headers->set('Content-Type', 'text/html; charset=UTF-8');
    $request->setRequestFormat('html');

    // Overwrite the original response
    $event->setResponse($response);
  }
}

Then you just need to register the listener inside your bundle to the kernel.response event, which I suggest you do only in the dev environment config.

services:
  # ...
  acme.listener.kernel.convert_html:
    class: Acme\APIBundle\Event\Listener\ConvertToHtmlResponse
    tags:
      - { name: kernel.event_listener, event: kernel.response }
Caveator answered 12/2, 2014 at 20:47 Comment(1)
Thank you :) Exactly what I wanted to get! I was aware of the conditions for the profiler to appear but I didn't know how to implement it properly. IMO, it's better than hacking the debug token in an iframe like suggested above.Bedwarmer
A
5

You can just open a seperate browser and browse to .../app_dev.php/_profiler/ there you will find all your requests done to app_dev.php including route matching, DB queries involved, etc.

Abject answered 20/7, 2017 at 11:49 Comment(0)
B
0

With the FOSRestBundle, I use a special template to display the data in an html page, therefore with the debug toolbar.

In my controller with annotations (you also use corresponding methods):

@View(template="AppBundle:Api:data.html.twig", templateVar="data")

And in the template, choosing whatever format you fancy:

<body>
    <pre>{{ data | serialize('json') }}</pre>
</body>

It is obviously a quick&dirty solution, but does the job. It also limits the ability to display actual html pages on those routes.

Bromide answered 5/12, 2014 at 14:15 Comment(0)
U
0

I use my WithProfilerTrait which applies the toolbar only if you navigate to the URL in the browser and skips the change, if it's an ajax request.

// in Controller:
return $this->withProfiler($response, $request->isXmlHttpRequest());
trait WithProfilerTrait
{
    protected function withProfiler(Response $response, bool $skip = false): Response
    {
        if ($skip === true) {
            return $response;
        }

        $wrappedContent = '<body><pre>' . $response->getContent() . '</pre></body>';
        $response->headers->set('Content-Type', 'html');
        return $response->setContent($wrappedContent);
    }
}
Unblinking answered 12/6, 2020 at 8:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.