Routing using a Controller - concrete5 Route Example - c5-8.5.2

Permalink
I have attempted to get the Developer Documentation example for Routing using a Controller to work but I am stuck. I just get the error message: "The requested URL was not found on this server. Additionally, a 404 Not Found error was encountered..."

Per the example the URL I use is: https://www.mysite.com/api/current_user... (mysite.com is changed to my real domain)

The examples I followed are found on the following two Developers Document pages:
https://documentation.concrete5.org/developers/routing/controllers...
https://documentation.concrete5.org/developers/extending-concrete5-w...

Here is my code:

/application/bootstrap/app.php (code is appended to the end of app.php)
$app = app();  // in c5-8.5.2+ app() is a Global function
$router = $app->make('router');
$router->get('/api/current_user', 'Application\Api\Controller\UserController::getCurrentUser');


/application/bootstrap/autoload.php (code is appended to the end of autoload.php)
$classLoader = new \Symfony\Component\ClassLoader\Psr4ClassLoader();
$classLoader->addPrefix('Application\\Api\\Controller', DIR_APPLICATION . '/' . DIRNAME_CLASSES . '/Api/Controller');
$classLoader->register();


application/src/Api/Controller/User.php
<?php
namespace Application\Api\Controller;
use Concrete\Core\User\User;
use Symfony\HttpFoundation\JsonResponse;
class UserController
{
    public function getCurrentUser()
    {
        $u = new User();
        if ($u->isRegistered()) {
            $data = [];
            $data['user_id'] = $u->getUserID();
            $data['username'] = $u->getUserName();
            return new JsonResponse($data);
        } else {

jfhencken
 
shahroq replied on at Permalink Reply
shahroq
A descriptive code as to how a route is registered is housed inside /application/bootstrap/app.php: line 28-41.
In your case, using this code should simply do the trick:
use Concrete\Core\User\User; 
use Symfony\Component\HttpFoundation\JsonResponse;
Route::register('/api/current_user', function() {
    $u = new User();
    if ($u->isRegistered()) {
        return new JsonResponse([
            'user_id' => $u->getUserID(), 
            'username' => $u->getUserName(),
        ]);
    }
    return new JsonResponse([], 500);
});

Or, should you insist on placing your code into a separate file, a file can be created preferably in:
\application\src\Api\Controller\UserApi.php
namespace Application\Api\Controller;
use Concrete\Core\User\User; 
use Symfony\Component\HttpFoundation\JsonResponse;
class UserApi
{
    public function getCurrentUser()
    {
        $u = new \User();
        if ($u->isRegistered()) {
            return new JsonResponse([
                'user_id' => $u->getUserID(), 
                'username' => $u->getUserName(),
            ]);
        }
        return new JsonResponse([], 500);
Then in app.php your route point out to the new class:
Route::register('/api/current_user', 'Application\Api\Controller\UserApi::getCurrentUser');

And don't forget to register the path in:
\application\bootstrap\autoload.php
$classLoader = new \Symfony\Component\ClassLoader\Psr4ClassLoader();
$classLoader->addPrefix('Application\\Api', DIR_APPLICATION . '/' . DIRNAME_CLASSES . '/Api');
$classLoader->register();


In either case, you should be able to hit the route with:
http://yoursite.com/index.php/api/current_user
jfhencken replied on at Permalink Reply
jfhencken
shahroq,

Thank you for helping me with this issue. I would really like to master this Routing coding…

In all cases I am logged in as SuperUser (admin).

First, I appended your example code:
$classLoader = new \Symfony\Component\ClassLoader\Psr4ClassLoader();
$classLoader->addPrefix('Application\\Api', DIR_APPLICATION . '/' . DIRNAME_CLASSES . '/Api');
$classLoader->register();

to the end of \application\bootstrap\autoload.php [ Is appending the code to the end of this file the correct place to put this code? ]

Then I placed your example code:
namespace Application\Api;
use Symfony\Component\HttpFoundation\JsonResponse;
class ApiUsers
{
    public function getCurrentUser()
    {
        $u = new \User();
        if ($u->isRegistered()) {
            return new JsonResponse([
                'user_id' => $u->getUserID(), 
                'username' => $u->getUserName(),
            ]);
        }
        return new JsonResponse([], 500);
    }

in \application\src\Api\ApiUsers.php [ I can't figure out how a URL even finds this code ]

Finally I appended your example code:
use Symfony\Component\HttpFoundation\JsonResponse;
Route::register('/testapi1', function() {
    $u = new \User();
    if ($u->isRegistered()) {
        return new JsonResponse([
            'user_id' => $u->getUserID(), 
            'username' => $u->getUserName(),
        ]);
    }
    return new JsonResponse([], 500);
});

to the end of /application/bootstrap/app.php [ Is this where this code should go? ]

I clicked on this link: http://mysite.com/testapi1 which took me to a blank page which returned:

“Not Found
The requested URL was not found on this server.
Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.”

I tried various combinations like removing the \application\src\Api\ApiUsers.php file in case it was interfering with the other, more basic solution, code.

I even tried the most basic example:
Route::register('/test', function() {
    print 'This is a contrived example.';
});

using the URL of http://mysite.com/test and http://mysite.com/api/test for Route::register('/api/test', function()…

Every combination returns a 404 Not Found.

There must be something very simple which I am missing since I assume this type of functionality gets used regularly by concrete5 developers.

How can I troubleshoot this code? Are there any fully coded real world examples of this functionality being used in an Add-On or in the c5 code?
shahroq replied on at Permalink Reply
shahroq
Did you get Apache 'Not Found' page or c5 404 page?
Placing code like this should definitely return your text:
Route::register('/test', function() {
    print 'This is a contrived example.';
    return '';
});

Did you also try the url with 'index.php' included?
mysite.com/index.php/test
jfhencken replied on at Permalink Reply 1 Attachment
jfhencken
I can't tell whether the error comes from Apache or c5

I get the following error when using mysite.com/test

Not Found
The requested URL was not found on this server.
Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.

Here is the error page script
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
<script type="text/javascript" src="http://ff.kis.v2.scr.kaspersky-labs.com/FD126C42-EBFA-4E12-B309-BB3FDD723AC1/main.js?attr=S59j59t_UP2HSYhTia2G8RKVh2qHf5oU_J3F6MT-lBiHi2DTHsywZW8HV55STXnIA0eUbNAhjQRElpnJPXtnNg" charset="UTF-8"></script></head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<p>Additionally, a 404 Not Found
error was encountered while trying to use an ErrorDocument to handle the request.</p>
</body></html>


when I saw the Kasperskey script I turned off the Kaspersky protection and tried it again - with the same results.

and I get the following error when using mysite.com/index.php/test

Error
Call to a member function has() on null

I have attached an image of the error screen.

Will using Postman help solve this problem?
shahroq replied on at Permalink Reply
shahroq
Hi,
The 'error page script' you have pasted seems like an apache error and, in turn, suggests the 'Pretty URL' is not enabled in your site (/index.php/dashboard/system/seo/urls) and 'index.php' should exist in your route.
& the error in the attached screenshot can be an indication of entering into the route callback method. Just paste this code into your app.php file and you should see contrived message:
Route::register('/testroute', function() {
    print 'This is a contrived example.';
    return '';
});
// the route would be: ursite.com/index.php/testroute

Make sure you have that `return` line at the end of your callback method and the result should appear.
jfhencken replied on at Permalink Reply 1 Attachment
jfhencken
Thank you for hanging in here with me.

Your first 2 examples are now working. Thank you. My attempt at using a controller is still not working.

Example 1 works when called at: https://mywebsite.com/index.php/testroute...
/* Code in: /application/bootstrap/app.php  */
Route::register('/testroute', function() {
  print 'This is a contrived example.';
  return '';
});

Example 2 works when called at: https://mywebsite.com/index.php/testapi...
/* Code in: /application/bootstrap/app.php  */
use Symfony\Component\HttpFoundation\JsonResponse;
Route::register('/testapi', function() {
    $u = new \User();
    if ($u->isRegistered()) {
        return new JsonResponse([
            'user_id' => $u->getUserID(), 
            'username' => $u->getUserName(),
        ]);
    }
    return new JsonResponse([], 500);
});

Example 3 does not work when called at: https://mywebsite.com/index.php/api/current_user...
I copied the example, with some mods based on your examples, from: https://documentation.concrete5.org/developers/routing/controllers...
/* Code in: /application/src/Api/Controller/User.php  */
namespace Application\Api\Controller;
use Concrete\Core\User\User;
use Symfony\Component\HttpFoundation\JsonResponse;  // per shahroq
class UserController
{
    public function getCurrentUser()
    {
        $u = new User();
        if ($u->isRegistered()) {
            $data = [];
            $data['user_id']  = $u->getUserID();
            $data['username'] = $u->getUserName();
            return new JsonResponse($data);
        } else {

and
/* Code in: /application/bootstrap/app.php  */
Route::register('/api/current_user', '\Application\Api\Controller\UserController::getCurrentUser');

I have attached an image of the error window. The basic error is:
"ReflectionException (-1) - Class \Application\Api\Controller\UserController does not exist"
Your help in solving this last problem would be much appreciated.
shahroq replied on at Permalink Reply
shahroq
In your code, the class name does not match the file name. Renaming `/application/src/Api/Controller/User.php` to `/application/src/Api/Controller/UserController.php` should fix that.
jfhencken replied on at Permalink Reply
jfhencken
I renamed User.php to UserController.php and it still does not work.

The Error is: " ReflectionException (-1) - Class \Application\Api\Controller\UserController does not exist"

The Class of UserController.php is UserController. And its namespace is Application\Api\Controller

The Documentation at:https://documentation.concrete5.org/developers/routing/controllers... says:

Let’s take our current_user route example. How about we move it out of a route, and into a dedicated PHP controller. This is how that’s done:

$router->get(/api/current_user’, ‘Application\Api\Controller\UserController::getCurrentUser);

We now no longer include the logic of retrieving the current user inline with the route definition. Instead, it is housed in a separate class, found at Application\Api\Controller\User. Furthermore, we have a specific method within this controller class that we run when the route is visited, getCurrentUser.

What does this controller look like? There isn’t much to it. At application/src/Api/Controller/User.php, we have:

<?php
namespace Application\Api\Controller;
use Concrete\Core\User\User;
use Symfony\HttpFoundation\JsonResponse;
/* use Symfony\Component\HttpFoundation\JsonResponse; // should be this per shahroq */
class UserController
{
    public function getCurrentUser()
    {
        $u = new User();
        if ($u->isRegistered()) {
            $data = [];
            $data[‘user_id’] = $u->getUserID();
            $data[‘username’] = $u->getUserName();
            return new JsonResponse($data);


In the documentation...

The Route registration says:
Application\Api\Controller\UserController::getCurrentUser

The location of the controller says:
application/src/Api/Controller/User.php (not UserController.php)

One has the src directory and the other does not, but I understand the src is no longer necessary in the namespace.
jfhencken replied on at Permalink Reply
jfhencken
Solved the problem when using a routing controller by place the following code in /application/bootstrap/autoload.php

$classLoader = new \Symfony\Component\ClassLoader\Psr4ClassLoader();
$classLoader->addPrefix('Application\\Api\\Controller', DIR_APPLICATION . '/' . DIRNAME_CLASSES . '/Api/Controller');
$classLoader->register();