Lumen POST Routes not Working - lumen

Consider the following:
$router->group([
'prefix' => 'api/v1/group',
'middleware' => 'auth'
], function () use ($router) {
$router->get('/', [
'as' => 'group.list',
'uses' => 'Api\V1\GroupController#list'
]);
$router->post('/', [
'as' => 'group.create',
'uses' =>'Api\V1\GroupController#create'
]);
$router->get('/{groupUUID}', [
'as' => 'group.retrieve',
'uses' =>'Api\V1\GroupController#retrieve'
]);
$router->put('/{groupUUID}', [
'as' => 'group.update',
'uses' => 'Api\V1\GroupController#update'
]);
});
As you can see, a pretty typical route setup. However, I'm seeing some incredibly odd behaviour - in short, the POST route seems to be being interpreted by the app as a GET route. When I make a POST request to api/v1/group (via Postman) I don't see the result of Api\V1\GroupController#create, but the result of Api\V1\GroupController#list.
I wondered if perhaps this was something to do with both routes having the same endpoint (shouldn't matter, but maybe it's different in Lumen? I usually work in full-on Laravel). So I commented out the get route. That made me just see a 404.
I then wondered if perhaps this entire route group was somehow broken. So I made two catchall endpoints:
$router->get('/{any:.*}', function () use ($router) {
return 'I am a get route';
});
$router->post('/{any:.*}', function () use ($router) {
return 'I am a post route';
});
And placed them at the top of the routes file, and commented out all other routes. Regardless of the route I hit or the method used, I always saw the same thing: I am a get route.
What's going on? What could cause my app to understand all POST requests as GET requests?
PS: It's also worth noting that these routes were working, until recently, without any real associated changes. Could something have been updated in a Lumen package that caused this?
PPS: I also tried using Insomnia instead of Postman, just in case it was a problem with Postman. Same result.

$router->get('/api/item/{table}/{id}', "ItemController#itemHandler");
$router->post('/api/item/{table}', "ItemController#itemHandler");
$router->put('/api/item/{table}/{id}', "ItemController#itemHandler");
$router->delete('/api/item/{table}/{id}', "ItemController#itemHandler");

I had pretty much the same issue. In my case - since I use Laravel Valet as development environment - I was able to make a POST request again, after serving the API locally over HTTP by executing valet unsecure my-project. On my production server, I still can use HTTPS, but for my local development environment, this solved the issue. Hope this helps some future readers.

Try api/v1/group/ (with trailing slash).

Related

Problem with plugin routes and multilanguage in a Cakephp4

I've built a multilanguage NewsManager plugin that has a NewsController
I'm try to write routes in this plugin to be able to access to routes like /en/news-manager/news/, /en/news-manager/news/my-lastest-news, ...
Here is my code :
// in /plugins/NewsManager/config/routes.php
$routes->scope('/{lang}', function (RouteBuilder $routes) {
$routes->plugin('NewsManager', function (RouteBuilder $routes) {
$routes->connect('/news', ['controller' => 'News', 'action' => 'index'])
->setPatterns([
'lang' => 'en|fr'
])
->setPersist(['lang']);
$routes->connect('/news/{slug}', ['controller' => 'News', 'action' => 'view'])
->setPass(['slug'])
->setPatterns([
'lang' => 'en|fr'
])
->setPersist(['lang']);
});
});
When I try to access to /en/news-manager/news/ I have a Missing Controller error with the message NewsManagerController could not be found.
What am I doing wrong ?
(Note that when I write exactly the same code that the code above but in my App routes it works...)
For parsing requests, routes are being ordered by their template's static path portion, which is the porition of the template before the first routing element.
So in your case the static portion would be / for all the language routes, as they immediately start with the {lang} routing element. This will result in a first comes first served order, meaning since your fallback routes are being connected before the plugin routes, they will be processed and matched first, making your plugin routes inaccessible.
As mentioned in the comments, one solution would obviously be to remove the fallback routes. This isn't an uncommon thing to do, as bailing out early at routes lookup will avoid unneccsary processing until the code runs into a problem at controller lookup later on.
Another way would be to connect the fallback routes after the plugin routes. Plugin route loading will always be invokved after application routes loading, so the pretty much only way to achieve this, is to move connecting the respective application routes into the plugin routes loading mechanism, which has a slight workaround smell.
Basically, move those routes into Application::pluginRoutes() to after invoking the parent method:
// in src/Application.php
public function pluginRoutes(\Cake\Routing\RouteBuilder $routes): \Cake\Routing\RouteBuilder
{
// connect plugin routes
$routes = parent::pluginRoutes($routes);
// connect fallback routes
$routes->scope('/{lang}', function (\Cake\Routing\RouteBuilder $routes) {
$routes->fallbacks();
});
return $routes;
}

CakePHP3 mapping RESTFul routes

I'm trying to config a RESTful API resources mapping in CakePHP3. I followed some tuts but can't make it works.
https://book.cakephp.org/3.0/en/development/routing.html#creating-restful-routes
http://www.bravo-kernel.com/2015/04/how-to-prefix-route-a-cakephp-3-rest-api/
I'm using 'prefix' route to map /api/v2/:resource with my sub-folders inside Controllers folder.
My file structure:
And this is my routing config
Router::prefix('api/v2', function ($routes) {
$routes->resources('Users');
// $routes->get('users', ['controller' => 'Users', 'action' => 'view']);
// $routes->post('users', ['controller' => 'Users', 'action' => 'create']);
// $routes->post('token', ['controller' => 'Users', 'action' => 'token']);
// $routes->fallbacks(DashedRoute::class);
});
As I read in the tuts i mention before, this should works, but i get a Missing Contoller Exception for ApiController.
If I uncomment the last line, enabling fallbacks it works fine, but it's not matching the Controller Methods with the HTTP method GET, POST, DELETE, PUT as CakePHP3 documentation mention.
Any ideas? My Cake version in 3.5.8
Thanks!!
EDIT: using bin/cake routes it's seems like the routes are fine. I'm using Postman to make request with differents HTTP methods to test this.
EDIT 2: I tried with another prefix Foo to avoid the V2 case sensitive issue, and well, this is extrage, the routes seems to be fine, but cake are not matching any of them..
Your folder name should be V2 not v2.

CAKEPHP3 REST API point to a different controller?

I believe I am missing something here. I am trying to point a route to a custom controller:
Router::scope('/myAPI/', function ($routes) {
$routes->extensions(['json', 'xml']);
$routes->resources('some_value',
[
'controller'=>'SomeValue',
'only' => ['index', 'create', 'update', 'delete'],
'id'=>'[0-9]+'
]);
});
Therefore the URL I am wanting is ~mydomain~/myAPI/some_value.
I would want this to point to the SomeValueController instead of looking for some_valueController. However the controller definition is ignored in this case and instead cakephp3 looks for some_valueController instead which I would rather avoid to try and keep with cakephp's naming conventions (and make use of the cake bake console to create a lot of controllers for models for me) but I would like to specify this custom URL.
What have I missed here?
Thanks,
Nevermind, if you TitleCase the name it works:
$routes->resources( 'SomeValue',
[
'only' => ['index', 'create', 'update', 'delete'],
'id'=>'[0-9]+'
]);
This allows the URL of some_value and points to the correct controller.
Posted just in case it helps someone else.

CakePHP Custom REST Routes

I'm using CakePHP and Backbone.js as a frontend so I want to get CakePHP's REST routing working, but I don't really want to use the default REST routes.
For example, I want to be able to POST to http://example.com/cards/search.json and get a list of results in JSON, however I am getting a 200 status code back, and a blank response which makes me think the routing is not working properly.
I have tested my code using the default REST routes by chagning the search() method of my controller to add(), but I would prefer to be able to properly setup and use custom REST routes.
Router::connect(
"/cards/search",
array(
"[method]" => "POST",
"controller" => "cards",
"action" => "search"
)
);
Router::mapResources('cards');
Router::parseExtensions('json');
The code from my routes.php is above and I'm not entirely sure why it isn't working...either because the documentation on this is a little light, or I just don't understand routing very well.
You can get the json output in this url:
http://localhost:{port}/{api* name in config # app/core}/{controller name}/{things after api_ in function name}/{input parameters}.json
read more in here
You may have to alter the routing to change the name
api
to anything in
Configure::write('Routing.prefixes', array('master', 'api'));
in core.php in app/config
Feel free for a comment and also share your core.php in config for more explanation.

Use route prefix with RESTful routes in CakePHP

Working on building an API and would like to use RESTful routes.
I got it to work just fine like this:
http://www.mysite.com/events.json // returns json results with my events
http://www.mysite.com/events/123.json // returns json results with event of id '123'
BUT - I want to be able to do this using an 'api' prefix.
So, I added the api Routing prefix:
Configure::write('Routing.prefixes', array('admin', 'api'));
And changed my actions from 'view' and 'index' to 'api_view' and 'api_index'.
But now it doesn't work. (eg. I have to write the action name or it won't find the correct one based on HTTP.
The end goal would be to be able to do something like this:
GET http://www.mysite.com/api/1.0/events.json // loads events/api_index()
GET http://www.mysite.com/api/1.0/events/123.json // loads events/api_view($id)
DELETE http://www.mysite.com/api/1.0/events/123.json // loads events/api_delete($id)
...etc
I ended up having to just write the routes manually:
Router::parseExtensions('json', 'xml');
Router::connect('/api/:version/:controller/:id/*',
array('[method]'=>'GET', 'prefix'=>'api', 'action'=>'view'),
array('version'=>'[0-9]+\.[0-9]+', 'id'=>'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'));
Router::connect('/api/:version/:controller/*',
array('[method]'=>'GET', 'prefix'=>'api', 'action'=>'index'),
array('version'=>'[0-9]+\.[0-9]+'));
Router::connect('/api/*', array('controller'=>'events', 'action'=>'index', 'ext'=>'html'));
Notes:
The [method] is what forces the HTTP type (eg. RESTful)
The parseExtensions() makes it so you can have it display the data in different formats automatically by changing the extension in your URL.
The last Router:: line was just a catchall for anything /api/ that didn't match - it forwarded it to the homepage. Eventually I'll probably just route this to an API error page.
The 'ext'=>'html' of the last Router:: line was to keep parseExtensions from trying to use whatever extension was in the URL - if it's redirecting for reasons they made the call wrong, I just want it to go back to the homepage (or whatever) and use the normal view.
Try something like this.
Router::connect('/:api/:apiVersion/:controller/:action/*',
array(),
array(
'api' => 'api',
'apiVersion' => '1.0|1.1|'
)
);
With prefix routing
Router::connect('/:prefix/:apiVersion/:controller/:action/*',
array(),
array(
'prefix' => 'api',
'apiVersion' => '1.0|1.1|'
)
);
Will match only valid API versions like 1.0 and 1.1 here. If you want something else use a regex there.
I know this is an old post, but there is a routing method called mapResources which creates the special method based routing for you.
http://book.cakephp.org/2.0/en/development/rest.html
You put it in routes.php like so:
Router::mapResources(array('controller1', 'controller2'));
The docs have a nice little table showing how the requests are mapped to different actions, which you can always override if you need to.