I am running into an issue with internal post requests using latest Laravel 5.2 and Dingo dev/master (commit 904d4e4e). Get requests work fine, but I keep getting a 'missing argument 1' error exception when I attempt any post request from anywhere (routes.php, and controllers).
My App Controllers all extend my Base Controller, which is set up as follows:
use Dingo\Api\Dispatcher;
use Dingo\Api\Routing\Helpers;
use Illuminate\Http\Request;
class BaseController extends Controller
{
use Helpers;
public $dispatcher;
public function __construct(Dispatcher $dispatcher)
{
$this->dispatcher = $dispatcher;
}
// My global functions (irrelevant)...
}
Example of admin subdomain 'modelExample' controller:
namespace App\Http\Controllers\Admin;
use App\Http\Requests;
use App\ModelExample;
use Dingo\Api\Dispatcher;
use App\Http\Controllers\BaseController;
class AdminModelExampleController extends BaseController
{
public function __construct(Dispatcher $dispatcher)
{
parent::__construct($dispatcher);
}
/**
* Display a listing of ModelExamples.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$modelExamples = $this->dispatcher->get('model-example');
return view('admin.model-examples.index')->with(['model_examples' => $modelExamples]);
}
/**
* Store a newly created ModelExample in the DB.
*
* #param $request
* #return array
*/
public function store($request)
{
$data = $request->all();
$modelExample = $this->dispatcher->post('model-example', $data);
return $modelExample;
}
}
Example of 'modelExample' API controller:
namespace App\Api\Controllers;
use App\Http\Requests;
use App\ModelExample;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
class ApiAddMediaMethodController extends BaseController
{
/**
* Return all ModelExamples.
*
* #return \Illuminate\Database\Eloquent\Collection|static[]
*/
public function index()
{
return ModelExample::all();
}
/**
* Store a newly created ModelExample in the DB.
*
* #param $request
* #return array
*/
public function store($request)
{
// I would obviously do stuff here, but for testing purposes, we'll just return...
return $request;
}
}
My routes.php
/*
|--------------------------------------------------------------------------
| Dingo generated router for API Routes
|--------------------------------------------------------------------------
*/
$api = app('Dingo\Api\Routing\Router');
$dispatcher = app('Dingo\Api\Dispatcher');
$api->version('v1', function ($api) {
// Set the namespace for the API_v1-specific route controllers
$api->group(['namespace' => 'App\Api\Controllers'], function ($api) {
//Model Example routes
$api->get('model-example', [
'as' => 'model-example.get',
'uses' => 'ApiModelExampleController#index'
]);
$api->post('model-example', [
'as' => 'model-example.post',
'uses' => 'ApiModelExampleController#store'
]);
});
});
/*
|--------------------------------------------------------------------------
| Application (non-API) Web Routes
|--------------------------------------------------------------------------
*/
Route::group(['middleware' => 'web'], function () {
// Bunch of regular web app routes (irrelevant)
/*
|--------------------------------------------------------------------------
| ADMIN Subdomain Routes
|--------------------------------------------------------------------------
*/
Route::group(['domain' => 'admin' . env('SESSION_DOMAIN')], function () {
// Set the namespace for the Admin Subdomain route controllers
Route::group(['namespace' => 'Admin'], function () {
Route::group(['middleware' => 'admin'], function(){
// Model Example routes
Route::resource('model-example', 'AdminModelExampleController');
}
}
}
}
No matter what I use in the store method on the Admin Controller, I get the following error:
Missing argument 1 for App\Api\Controllers\ApiModelExampleController::store()
I have tried:
$modelExample = $this->dispatcher->with($data)->post('model-example');
$modelExample = $this->dispatcher->post('model-example', ['hard' => 'coded', 'just' => 'in case']);
$modelExample = $this->api->post('model-example', $data);
$modelExample = $this->api->with($data)->post('model-example');
$modelExample = $this->api->post('model-example', ['hard' => 'coded', 'just' => 'in case']);
I have also tried directly from the routes file with test endpoints, I have turned off all middleware... no matter what I do, the payload array is not being recognized.
Is this a Laravel 5.2 vs 5.1 issue? Am I missing something completely obvious (been known to happen ...often)? How can I resolve this? Much thanks in advance! :-)
SOLVED.
It was my bad.
The resolution (for future searchers) was actually quite simple.
The data that is passed along with the Post request MUST be a Request instance (i.e., Illuminate/Http, or Form Request). A simple array or non-request object will not work. I hope this saves someone else from pulling their hair out. :-)
Related
I'm making a website project using Laravel Jetstream with stack inertia. In this project, I added the package spatie/laravel-permission. When I try to authorize the controller to protect against malicious users from accessing the URL directly I create a trait that I can call for any controller I want and here is the code
trait Authorizable
{
private $abilities = [
'index' => 'view',
'edit' => 'edit',
'show' => 'view',
'update' => 'edit',
'create' => 'add',
'store' => 'add',
'destroy' => 'delete',
];
/**
* Override of callAction to perform the authorization before
*
* #param $method
* #param $parameters
* #return mixed
*/
public function callAction($method, $parameters)
{
if ($ability = $this->getAbility($method)) {
$this->authorize($ability);
}
return parent::callAction($method, $parameters);
}
public function getAbility($method)
{
$routeName = explode('.', \Request::route()->getName());
$action = Arr::get($this->getAbilities(), $method);
return $action ? $action . '_' . $routeName[0] : null;
}
private function getAbilities()
{
return $this->abilities;
}
public function setAbilities($abilities)
{
$this->abilities = $abilities;
}
}
after that, I call the trait on the controller
use App\Http\Controllers\Controller;
use App\Traits\Authorizable;
use Illuminate\Http\Request;
use Inertia\Inertia;
class UsersController extends Controller
{
use Authorizable;
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
return Inertia::render('Admin/Users/Index');
}
this is my route
Route::middleware(['auth:sanctum', config('jetstream.auth_session'), 'verified',])->group(function () {
Route::middleware(['role:Admin'])->prefix('admin')->group(function () {
Route::resource('users', AdminUsers::class, ['as' => 'admin']);
});
});
but unfortunately, after I try to access the URL /admin/users, I get a message 403: THIS ACTION IS UNAUTHORIZED.
I have no idea to solve this problem, could anyone here to help me to solve this problem or give me some advice? it will save me.
Thank You
I have different domains with different routes in database and I want send every domain to a specific route.
How can I do that?
In which file I can get data from database that occur before route to decide which controller and method calls.
Can I have a variable ($domains) in web.php like this :
foreach ($domains as $domain) {
Route::group(array('domain' => $domain['domain']), function() use ($domain) {
Route::get('/', '' . $domain['controller'] . '#' . $domain['method']);
});
}
You should use the service provider (boot) for that. Go to /app/Providers/AppServiceProvider.php and dispatch the following code:
<?php
namespace App\Providers;
use App\MyDomainsModel;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
// get all domains
$Domains = MyDomainsModel::all();
// register the routes
foreach($Domains as $Domain)
Route::group(['domain' => $Domain['domain']] , function()use($Domain) {
Route::get('/', $Domain['controller'].'#'.$Domain['method']);
});
}
}
I want to show some user data by id. Simple.
user/{id} -> get data from a Controller's method.
I have this:
Route::group(['prefix' => 'user/{id}', 'where' => ['id' => '[0-9]+']], function() {
Route::get('delete', 'UserController#delete')->name('admin.access.user.delete-permanently');
Route::get('restore', 'UserController#restore')->name('admin.access.user.restore');
Route::get('mark/{status}', 'UserController#mark')->name('admin.access.user.mark')->where(['status' => '[0,1]']);
Route::get('password/change', 'UserController#changePassword')->name('admin.access.user.change-password');
Route::post('password/change', 'UserController#updatePassword')->name('admin.access.user.change-password');
});
How can I access a method as a default for user/{id}?
You can do this in your controller
<?php
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* Show the profile for the given user.
*
* #param int $id
* #return Response
*/
public function show($id)
{
//Fetch and send the user to the "profile" blade file in the folder "resources/views/user"
return view('user.profile', ['user' => User::findOrFail($id)]);
}
public function changePassword($id)
{
$user = User::findOrFail($id);
return $user->update(
'password' => bcrypt(123456)
);
}
}
I am using Laravel 5.3 in which using Authfor user controller.
So basically i create a Listener for Auth Event
'Illuminate\Auth\Events\Login' => [
'App\Listeners\LogSuccessfulLogin',
],
and in LogSuccessfulLogin handle() function redirecting user on basic of role. but my redirecting function is not working. Its render the default page '\home' route.
I am sharing My Files :-
EventServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
'App\Events\SomeEvent' => [
'App\Listeners\EventListener',
],
'Illuminate\Auth\Events\Login' => [
'App\Listeners\LogSuccessfulLogin',
],
];
/**
* Register any events for your application.
*
* #return void
*/
public function boot()
{
parent::boot();
//
}
}
Route/web.php
<?php
//Auth::routes();
Route::get('logout', function(){
Auth::logout(); // logout user
return Redirect::to('/');
});
#Route::get('/', 'HomeController#index')->name('homepage');
Route::get('/', function () {return view('welcome');});
Route::group(['middleware' => ['auth', 'checkrole:Admin'],'namespace' => 'Admin','prefix' => 'admin'], function () {
Route::get('/123','DashboardController#index')->name('dashboard');
#Route::get('/','DashboardController#index')->name('dashboard');
Route::get('user/profile', function () {
// Uses Auth Middleware
});
});
and here is LogSuccessfulLogin.php
<?php
namespace App\Listeners;
use Illuminate\Auth\Events\Login;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
class LogSuccessfulLogin
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param Login $event
* #return void
*/
public function handle(Login $event)
{
$user=Auth::User()->role->role_name;
if($user=="Admin"){
return Redirect::route('dashboard');
#return redirect()->route('login');
}
elseif($user == "Employer" ){
dd("hello");
}
}
}
and this is the default logincontroller of Auth
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* #var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest', ['except' => 'logout']);
}
}
Please let me know if i am doing some wrong.
Actually i also tried in LogSuccessfulLogin Listener for redirect the page with return redirect()->route('login'); but its also not working.
As one of possible solutions I would provide a response middleware that will be attached to login route. After getting response you should check if user is logged and if yes - apply your custom redirection login. In that case you will overwrite default Laravel 5.3 logic of redirection. Smth like this:
class PostLoginMiddleware
{
public function handle($request, Closure $next, $guard = null)
{
// here you've already have response and it's usually redirect
$response = $next($request);
// if user logged in
if (\Auth::id()) {
$customRedirectResponse = // ...apply your custom logic
return $customRedirectResponse;
}
return $response;
}
}
You should define this middleware in app/Http/Kernel.php in
protected $routeMiddleware = [
'custom-redirect' => \YourNamespace\PostLoginMiddleware::class
];
And then apply it on login route
/* Authentication Routes */
$this->get('login', 'Auth\AuthController#showLoginForm');
$this->post('login', ['middleware' => 'custom-redirect', 'uses' => 'Auth\AuthController#login']);
$this->get('logout', 'Auth\AuthController#logout');
I have a controller with the following in the constructor:
$this->middleware('guest', ['except' =>
[
'logout',
'auth/facebook',
'auth/facebook/callback',
'auth/facebook/unlink'
]
]);
The 'logout' rule (which is there by default) works perfectly but the other 3 rules I have added are ignored. The routes in routes.php look like this:
Route::group(['middleware' => ['web']],function(){
Route::auth();
// Facebook auth
Route::get('/auth/facebook', 'Auth\AuthController#redirectToFacebook')->name('facebook_auth');
Route::get('/auth/facebook/callback', 'Auth\AuthController#handleFacebookCallback')->name('facebook_callback');
Route::get('/auth/facebook/unlink', 'Auth\AuthController#handleFacebookUnlink')->name('facebook_unlink');
}
If I visit auth/facebook, auth/facebook/callback or auth/facebook/unlink whilst logged in I get denied by the middleware and thrown back to the homepage.
I've tried specifying the 'except' rules with proceeding /'s so they match the routes in routes.php exactly but it makes no difference. Any ideas why these rules are being ignored, whilst the default 'logout' rule is respected?
Cheers!
You need to pass the method's name instead of the URI.
<?php
namespace App\Http\Controllers;
class MyController extends Controller {
public function __construct() {
$this->middleware('guest', ['except' => [
'redirectToFacebook', 'handleFacebookCallback', 'handleFacebookUnlink'
]]);
}
}
Since Laravel 5.3, you can use fluent interface to define middlewares on controllers, which seems cleaner than using multidimensional arrays.
<?php
$this->middleware('guest')->except('redirectToFacebook', 'handleFacebookCallback', 'handleFacebookUnlink');
I solved this issue in my Middleware by adding this inExceptArray function. It's the same way VerifyCsrfToken handles the except array.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class MyMiddleware
{
/**
* Routes that should skip handle.
*
* #var array
*/
protected $except = [
'/some/route',
];
/**
* Determine if the request has a URI that should pass through.
*
* #param Request $request
* #return bool
*/
protected function inExceptArray($request)
{
foreach ($this->except as $except) {
if ($except !== '/') {
$except = trim($except, '/');
}
if ($request->is($except)) {
return true;
}
}
return false;
}
/**
* Handle an incoming request.
*
* #param Request $request
* #param Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
// check user authed or API Key
if (!$this->inExceptArray($request)) {
// Process middleware checks and return if failed...
if (true) {
// Middleware failed, send back response
return response()->json([
'error' => true,
'Message' => 'Failed Middleware check'
]);
}
}
// Middleware passed or in Except array
return $next($request);
}
}
If you are trying to follow the Laravel Documentation, an alternative solution to this is suggested by adding routes to the $except variable in the /Http/Middleware/VerifyCsrfToken.php file. The documentation says to add them like this:
'route/*'
But I found the only way to get it to work is by putting the routes to ignore like this:
'/route'
When assigning middleware to a group of routes, you may occasionally need to prevent the middleware from being applied to an individual route within the group. You may accomplish this using the withoutMiddleware method:
use App\Http\Middleware\CheckAge;
Route::middleware([CheckAge::class])->group(function () {
Route::get('/', function () {
//
});
Route::get('admin/profile', function () {
//
})->withoutMiddleware([CheckAge::class]);
});
for more information read documentation laravel middleware
Use this function in your Controller:
public function __construct()
{
$this->middleware(['auth' => 'verified'])->except("page_name_1", "page_name_2", "page_name_3");
}
*replace page_name_1/2/3 with yours.
For me it's working fine.
I have this solved, and here's what I am doing. Aso, I just realized this is very similar to what cmac did in his answer.
api.php
Route::group(['middleware' => 'auth'], function () {
Route::get('/user', 'Auth\UserController#me')->name('me');
Route::post('logout', 'Auth\LoginController#logout')->name('logout');
});
LoginController.php
class LoginController extends Controller
{
use AuthenticatesUsers, ThrottlesLogins;
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
// ...
/**
* If the user's session is expired, the auth token is already invalidated,
* so we just return success to the client.
*
* This solves the edge case where the user clicks the Logout button as their first
* interaction in a stale session, and allows a clean redirect to the login page.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function logout(Request $request)
{
$user = $this->guard()->user();
if ($user) {
$this->guard()->logout();
JWTAuth::invalidate();
}
return response()->json(['success' => 'Logged out.'], 200);
}
}
Authenticate.php
class Authenticate extends Middleware
{
/**
* Exclude these routes from authentication check.
*
* Note: `$request->is('api/fragment*')` https://laravel.com/docs/7.x/requests
*
* #var array
*/
protected $except = [
'api/logout',
];
/**
* Ensure the user is authenticated.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
try {
foreach ($this->except as $excluded_route) {
if ($request->path() === $excluded_route) {
\Log::debug("Skipping $excluded_route from auth check...");
return $next($request);
}
}
// code below here requires 'auth'
{ catch ($e) {
// ...
}
}
I over-engineered it slightly. Today I only need an exemption on /api/logout, but I set the logic up to quickly add more routes. If you research the VerifyCsrfToken middleware, you'll see it takes a form like this:
protected $except = [
'api/logout',
'api/foobars*',
'stripe/poop',
'https://www.external.com/yolo',
];
That's why I put that "note" in my doc above there. $request->path() === $excluded_route will probably not match api/foobars*, but $request->is('api/foobars*') should. Additionally, a person might be able to use something like $request->url() === $excluded_route to match http://www.external.com/yolo.
You should pass the function name to 'except'.
Here's an example from one of my projects:
$this->middleware('IsAdminOrSupport', ['except' => [
'ProductsByShopPage'
]
]);
This means the middleware 'IsAdminOrSupport' is applied to all methods of this controller except for the method 'ProductByShopPage'.