Cody Fendant has asked for the wisdom of the Perl Monks concerning the following question:

I posted a more specific question earlier but I'll ask this more general question, hope this is OK…

How do I debug Catalyst when a GET request to a URL works but a POST request to a URL fails?

I've got debugging on and I can see my URL, or action, or route or whatever we should call it, loading like this:

[debug] Loaded Private actions: [snip] | Private | Class | Method | /user/update | MyApp::Controller::User | update [snip]

Then I can also see this:

[debug] Loaded Chained actions: [snip] | Path Spec | Private | /user/*/update | -> /user/chain (1) | | => /user/update (0) [snip]

Which makes sense, because the URL is myapp.com/user/<a given user's id>/update

But I still have my problem which is that when I do a GET to that URL, everything works. But when I do a POST to that URL, I get an error, actually a 404.

Inside the update method in the Controller, I have something like this:

if($c->req->method eq 'POST'){ ### update a database }

But the code appears to be failing before that ever runs, as soon as the POST request is made.

How do I debug this? In another type of application there would be a routing table specifying that a given URL accepts GETs or POSTs or both. But that's not how Catalyst works? How can I verify that a POST to that URL, and implicitly to that Controller and Action, is even allowed? And/or how can I debug what happens specifically when a POST is made and Catalyst throws an error?

More detail: the debug log shows just this:

.--------------------------------------------------------------------- +-------------------------------+-----------. | Action + | Time | +--------------------------------------------------------------------- +-------------------------------+-----------+ | user/<my user name>/update - start + | 0.024544s | | /auto + | 0.002635s | | -> /error_template + | 0.000193s | | /end + | 0.020821s |

so it performed the right Action, then 'auto' took over and it failed at that point?

TIA

Replies are listed 'Best First'.
Re: Help me understand Catalyst routing, or do I mean Actions?
by NERDVANA (Priest) on Jun 15, 2022 at 02:29 UTC
    In general, a Catalyst application should dispatch all HTTP methods to a matching action, based only on the path. So, the code you posted which checks $c->req->method is a correct approach and should work. Since it doesn't work, the next question is whether you have any special middleware or Catalyst plugins that are affecting things. I didn't answer your previous question in hopes that someone would recognize the specific scenario, but I guess nobody did.

    One of the powerful features of Catalyst is the great degree of "hooking" that you can do at all phases of handling the request. The downside is that a lot of the flow control of the application becomes hidden and makes a learning curve for the maintenance developer. A developer or plugin author might decide to make POST requests opt-in rather than manually switching inside of each action, or something like that. Your challenge is to find out where that happened.

    First, check all the plugins loaded at the top level

    use Catalyst qw/ PLUGIN1 PLUGIN2 PLUGIN3 ... /;
    and read their CPAN documentation.

    Then, check for the base class of the controller.

    use Moose; extends 'Catalyst::Controller'; # normal # vs... extends 'Catalyst::Controller::REST'; # changes behavior of things
    Sometimes a project might also have a 'MyApp::Controller' base class used for all controllers where the author sets up the defaults they want to use for the project.

    Check for the 'begin' and 'end' actions:

    sub begin :SOME_MAGIC_ATTRIBUTE {}
    or something more deliberate:
    sub begin { my ($self, $c)= @_; if ($c->req->method ne 'GET') { $c->res->code(404); $c->detch; }

    Then, check for behaviors of a parent controller, such as the root controller:

    package MyApp::Controller::Root; sub begin { ... }
    and any controller in the filesystem hierarchy leading to the one that is giving you problems.

    You can also put debug statements in each of the begin/end places to help log what is going on.

    Hope that helps.

      Thanks so much, that's very helpful and thorough. Putting all that into practice now.