<p dir="auto">When a route doesn't match the <code class="notranslate">method</code> part of it an exception will be thrown. And the whole exception handling will be invoked. But if a route path is specified as <code class="notranslate">/path.{_format}</code> and the method is not allowed. The request given to listeners will always have <code class="notranslate">null</code> or <code class="notranslate">html</code> as the format. This makes it nearly impossible to return output based on what was requested.</p>
<p dir="auto">Hi all,</p>
<p dir="auto">It seems like a Symfony limitation or bug (in our case a security case).</p>
<p dir="auto">We use the <code class="notranslate">_format</code> for some validations and reports even when a Exception is thrown. But apparently it is not trusty all time. In one of the cases of use, we use the _format to generate the Exception redering according with requested format, but the format is ignored when the <code class="notranslate">_method</code> is not accepted.</p>
<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="product_option_create:
    pattern: /create.{_format}
    defaults:
        _format: ~
        _controller: controller.option:createAction
    requirements:
        _method: POST"><pre class="notranslate"><span class="pl-ent">product_option_create</span>:
    <span class="pl-ent">pattern</span>: <span class="pl-s">/create.{_format}</span>
    <span class="pl-ent">defaults</span>:
        <span class="pl-ent">_format</span>: <span class="pl-c1">~</span>
        <span class="pl-ent">_controller</span>: <span class="pl-s">controller.option:createAction</span>
    <span class="pl-ent">requirements</span>:
        <span class="pl-ent">_method</span>: <span class="pl-s">POST</span></pre></div>
<p dir="auto">The Symfony router cache system generates a cached <code class="notranslate">UrlMatcher</code> ( <em>appdevUrlMatcher</em> ) that has a method <code class="notranslate">match($pathinfo)</code> which verifies which cached URL matches with the current URL (including the <code class="notranslate">_method</code>) and returns the <code class="notranslate">preg_match</code> result to be merged with the context parameters. The <code class="notranslate">preg_match</code> result has all variables declared in the routes configurations populated with the URL data. The problem is when the parameter <code class="notranslate">_method</code> of route does not match with the request type, even then nothing is returned because a <code class="notranslate">MethodNotAllowedHttpException</code> is thrown.</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="#app/cache/dev/appdevUrlMatcher.php
public function match($pathinfo)
{
    if (0 === strpos($pathinfo, '/product/option/show') &amp;&amp; preg_match('#^/product/option/show(?:\\.(?P&lt;_format&gt;json))?$#s', $pathinfo, $matches)) {
        if ($this-&gt;context-&gt;getMethod() != 'POST') {
            // print_r($matches);
            // Array ( [0] =&gt; /product/option/show.json [_format] =&gt; json [1] =&gt; json )
            $allow[] = 'POST';
            goto not_product_option_show;
        }

        return $this-&gt;mergeDefaults(array_replace($matches, array('_route' =&gt; 'product_option_show')), array (  '_format' =&gt; NULL,  '_controller' =&gt; 'product.controller.option:showAction',));
    }
    not_product_option_show:

    throw 0 &lt; count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); //
}"><pre class="notranslate"><span class="pl-c">#app/cache/dev/appdevUrlMatcher.php</span>
<span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">match</span>(<span class="pl-s1"><span class="pl-c1">$</span>pathinfo</span>)
{
    <span class="pl-k">if</span> (<span class="pl-c1">0</span> === strpos(<span class="pl-s1"><span class="pl-c1">$</span>pathinfo</span>, <span class="pl-s">'/product/option/show'</span>) &amp;&amp; preg_match(<span class="pl-s">'#^/product/option/show(?:\\.(?P&lt;_format&gt;json))?$#s'</span>, <span class="pl-s1"><span class="pl-c1">$</span>pathinfo</span>, <span class="pl-s1"><span class="pl-c1">$</span>matches</span>)) {
        <span class="pl-k">if</span> (<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">context</span>-&gt;<span class="pl-en">getMethod</span>() != <span class="pl-s">'POST'</span>) {
            <span class="pl-c">// print_r($matches);</span>
            <span class="pl-c">// Array ( [0] =&gt; /product/option/show.json [_format] =&gt; json [1] =&gt; json )</span>
            <span class="pl-s1"><span class="pl-c1">$</span>allow</span>[] = <span class="pl-s">'POST'</span>;
            goto not_product_option_show;
        }

        <span class="pl-k">return</span> <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">mergeDefaults</span>(array_replace(<span class="pl-s1"><span class="pl-c1">$</span>matches</span>, <span class="pl-en">array</span>(<span class="pl-s">'_route'</span> =&gt; <span class="pl-s">'product_option_show'</span>)), <span class="pl-en">array</span> (  <span class="pl-s">'_format'</span> =&gt; <span class="pl-c1">NULL</span>,  <span class="pl-s">'_controller'</span> =&gt; <span class="pl-s">'product.controller.option:showAction'</span>,));
    }
    not_product_option_show:

    <span class="pl-k">throw</span> <span class="pl-c1">0</span> &lt; count(<span class="pl-s1"><span class="pl-c1">$</span>allow</span>) ? <span class="pl-k">new</span> <span class="pl-v">MethodNotAllowedException</span>(array_unique(<span class="pl-s1"><span class="pl-c1">$</span>allow</span>)) : <span class="pl-k">new</span> <span class="pl-v">ResourceNotFoundException</span>(); <span class="pl-c">//</span>
}</pre></div>
<p dir="auto">Thus, the method <code class="notranslate">getRequestFormat</code> returns <code class="notranslate">html</code> once the  <code class="notranslate">Request-&gt;format</code> property is <code class="notranslate">null</code>.</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="#Symfony\Component\HttpFoundation\Request
public function getRequestFormat($default = 'html')
{
    #echo $this-&gt;format; // returns 'html'
    if (null === $this-&gt;format) {
        $this-&gt;format = $this-&gt;get('_format', $default);
    }

    return $this-&gt;format;
}"><pre class="notranslate"><span class="pl-c">#Symfony\Component\HttpFoundation\Request</span>
<span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">getRequestFormat</span>(<span class="pl-s1"><span class="pl-c1">$</span>default</span> = <span class="pl-s">'html'</span>)
{
    <span class="pl-c">#echo $this-&gt;format; // returns 'html'</span>
    <span class="pl-k">if</span> (<span class="pl-c1">null</span> === <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">format</span>) {
        <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">format</span> = <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'_format'</span>, <span class="pl-s1"><span class="pl-c1">$</span>default</span>);
    }

    <span class="pl-k">return</span> <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">format</span>;
}</pre></div>
<p dir="auto">As the format is <code class="notranslate">html</code>, the method <code class="notranslate">ExceptionController-&gt;showAction</code> understands that is a case of templating format and renders a html page error instead of JSON serialization.</p>
<p dir="auto">Maybe a possible solution is to pass the <code class="notranslate">$matches</code> embedded in the <code class="notranslate">MethodNotAllowedException</code>, but there is a discussion about it on <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="9925685" data-permission-text="Title is private" data-url="https://github.com/FriendsOfSymfony/FOSRestBundle/issues/358" data-hovercard-type="issue" data-hovercard-url="/FriendsOfSymfony/FOSRestBundle/issues/358/hovercard" href="https://github.com/FriendsOfSymfony/FOSRestBundle/issues/358">FriendsOfSymfony/FOSRestBundle#358</a></p>