Is there a way to turn off some Laravel 5.0 middleware for functional tests?
Just edit app/Http/kernel.php file and comment any undesired middleware line in array $middleware.
You don't need to comment the ones in $routeMiddleware since these won't be automatically called, and they need to be specifically activated in the routes.php file.
Another way:
Copy Kernel.php as Kerneltest.php in the same folder.
Then rename the class in Kerneltest.php to Kerneltestand make it extends Kernel.
Then remove any middleware lines from Kerneltest
Then add the following to bootstrap\app.php :
$app->singleton(
'Illuminate\Contracts\Http\Kerneltest',
'App\Http\Kerneltest'
);
Then in public\index.php use
$kernel = $app->make('Illuminate\Contracts\Http\Kerneltest');
or
$kernel = $app->make('Illuminate\Contracts\Http\Kernel');
depending on whether you're testing or not.
I've found simple solution, although probably it may be not "TRUE", but it works. I've modified method handle() in my app/Http/Middleware/VerifyCsrfToken.php to this
public function handle($request, Closure $next)
{
if (env('APP_ENV') === 'testing') {
return $next($request);
}
return parent::handle($request, $next);
}
Related
I'm working on a Laravel based project and I need to execute some "basic" php code on every page load. Until now, I placed my code in boot() from AppServiceProvider. It works well, but I need to execute my "basic" code only after the code from route's controller has been already executed.
I've already searched in laravel's official docs, but I still did not figured out how to do it.
This is how my route looks:
Route::match(['get', 'post'], '/profile/email/{profile_id?}', 'profileController#profileHandleEmail')->name('profile/email');
The result I want to achive is to execute the code from profileController#profileHandleEmail before the "basic" code from AppServiceProvider.
Which would be the best way to do this? I guess it can't be achived using AppServiceProvider.
The suggested way to achieve what you want is to use middleware:
Run php artisan make:middleware PostProcess
It should generate the a middleware class under App\Http\Middleware
class PostProcess {
public function handle($request, $next) {
$response = $next($request);
// Run you code here.
return $response
}
}
Then modify your App\Http\Kernel.php middleware:
protected $middleware = [
//Existing entries
\App\Http\Middleware\PostProcess::class
];
That middleware will run after the response has been generated but before the response is sent to the client. If you want to run the code after the response was sent to the client you can use terminable middleware
class PostProcess {
public function handle($request, $next) {
return $next($request);
}
public function terminate($request, $response) {
//Your code here
}
}
Step1 : Create Middleware
php artisan make:middleware PostProcess
Step2 : Make a code which you need
class PostProcess {
public function handle($request, $next) {
if(condition){
you code here
}
return $next($request);
}
}
Step3 : Call middleware in kernel.php
protected $routeMiddleware = [
'admin' => \App\Http\Middleware\PostProcess::class,
];
Step 4: Call middleware in route file
Route::group(['middleware' => 'admin'], function() {
});
I have the following code in a custom middleware:
public function handle($request, Closure $next)
{
if($request->hasCookie('uuid')) {
return $next($request);
}
$uuid = 99;
$response = $next($request);
return $response->withCookie(cookie()->forever('uuid', $uuid));
}
I have registered the middleware in the app.php file, but cookies is still not being written. Please can anyone help. Additionally can this above be run as a singleton, so that it is executed once on app start?
Thanks
If you are on a local environment, make sure to set 'secure' => env('SESSION_SECURE_COOKIE', false), around lines #165-170 in your config/session.php file.
On a non-SSL domain (like http://localhost/), this directive must be false. Otherwise cookies will not be set in browser.
Here, I'm mention set and get a cookie in laravel simple example following.
First of the create a controller.
1.php artisan make:controller CookieController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class CookieController extends Controller {
**/* below code a set a cookie in browser */**
public function setCookie(Request $request){
$response = new Response('Hello World');
$response->withCookie(cookie('name', 'Anything else'));
return $response;
}
**/* below code a get a cookie in browser */**
public function getCookie(Request $request){
$value = $request->cookie('name');
echo $value;
}
}
Add a following line code in routes/web.php file (Laravel 5.4)
Route::get('/cookie/set','CookieController#setCookie');
Route::get('/cookie/get','CookieController#getCookie');
And all files add-in project than a run program easily sets and get a cookie.
You can obtain the response object in middleware like so:
public function handle($request, Closure $next)
{
$response = $next($request);
// Do something after the request is handled by the application
return $response;
}
So you could do something like this
if($request->hasCookie('uuid')) {
return $next($request);
}
$uuid = Uuid::generate();
$response = $next($request);
return $response->withCookie(cookie()->forever('uuid', $uuid));
You can use the laravel helper function cookie(). This worked for me.
<?php
//Create:
cookie()->queue(cookie($name, $value, $minutes));
// forever
cookie()->queue(cookie()->forever($name, $value));
//get
request()->cookie($name);
//forget
cookie()->queue(cookie()->forget($name));
I want to test the delete method but I am not getting the expected results from PHPUnit. I receive this message when running the test:
Expected status code 200 but received 419. Failed asserting that false is true.
/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:77
/tests/Unit/CategoriesControllerTest.php:70
Laravel version: 5.5
Thank you for any help!
Controller constructor:
public function __construct()
{
$this->middleware('auth');
$this->middleware('categoryAccess')->except([
'index',
'create'
]);
}
Controller method:
public function destroy($categoryId)
{
Category::destroy($categoryId);
session()->flash('alert-success', 'Category was successfully deleted.');
return redirect()->action('CategoriesController#index');
}
categoryAccess middleware:
public function handle($request, Closure $next)
{
$category = Category::find($request->id);
if (!($category->user_id == Auth::id())) {
abort(404);
}
return $next($request);
}
Category model:
protected $dispatchesEvents = [
'deleted' => CategoryDeleted::class,
];
Event listener
public function handle(ExpensesUpdated $event)
{
$category_id = $event->expense->category_id;
if (Category::find($category_id)) {
$costs = Category::find($category_id)->expense->sum('cost');
$category = Category::find($category_id);
$category->total = $costs;
$category->save();
}
}
PHPUnit delete test:
use RefreshDatabase;
protected $user;
public function setUp()
{
parent::setUp();
$this->user = factory(User::class)->create();
$this->actingAs($this->user);
}
/** #test */
public function user_can_destroy()
{
$category = factory(Category::class)->create([
'user_id' => $this->user->id
]);
$response = $this->delete('/category/' . $category->id);
$response->assertStatus(200);
$response->assertViewIs('category.index');
}
Solution: When you cached your configuration files you can resolve this issue by running php artisan config:clear.
Explanation: The reason why this can resolve the issue is that PHPUnit will use the cached configuration values instead of the variables defined in your testing environment. As a result, the APP_ENV is not set to testing, and the VerifyCsrfTokenMiddleware will throw a TokenMismatchException (Status code 419).
It won't throw this exception when the APP_ENV is set to testing since the handle method of VerifyCsrfTokenMiddleware checks if you are running unit tests with $this->runningUnitTests().
It is recommended not to cache your configuration in your development environment. When you need to cache your configuration in the environment where you are also running unit tests you could clear your cache manually in your TestCase.php:
use Illuminate\Support\Facades\Artisan;
public function createApplication()
{
....
Artisan::call('config:clear')
....
}
Example based on https://github.com/laravel/framework/issues/13374#issuecomment-239600163
Read more about configuration caching in this blog post or the Laravel documentation.
Sometimes in testing you will need to disable middlewares to proceed :
use Illuminate\Foundation\Testing\WithoutMiddleware;
class ClassTest extends TestCase
{
use WithoutMiddleware; // use this trait
//tests here
}
and if you want to disable them just for one specific test use :
$this->withoutMiddleware();
The message here is indeed related to the CSRF middleware. But there is a much better way of attacking this problem than disabling the middleware.
The middleware comes with code built-in that detects if it is being used in a test. This check looks for 2 things:
Am I being ran via a command line
Am I being ran in an environment type of testing
Default, proper setup of the software correctly causes both flags to be true when running PHP unit. However, the most likely culprit is the value in your APP_ENV. Common ways for this to to be incorrect include:
Misconfigured phpunit.xml file. It should contain <server name="APP_ENV" value="testing" />
A shell session that has an explicit value set in APP_ENV that overrides this value
A Docker/docker-compose/kubernetes session that has an explicit value set in APP_ENV. Seeing about getting this value set via the .env and/or phpunit.xml files is perhaps better if possible. Or ensuring the build/test process sets the value.
This one stumped me as well and I was not convinced that I would need the use of WithoutMiddleware since I did not for a different project, but it turned out I had experimented with something on the command line and overrode APP_ENV in bash.
This is the exact solution:
Laravel environment will set after bootstraping application, so you cant change it from appServiceProvider or another source.
for fix this error you need to add this function to App\Http\Middleware\VerifyCsrfToken
public function handle($request, \Closure $next)
{
if(env('APP_ENV') !== 'testing')
{
return parent::handle($request, $next);
}
return $next($request);
}
you need to use env('APP_ENV') that is set in .env.testing file with
APP_ENV=testing
modify the file app/Http/Middleware/VerifyCsrfToken.php by adding:
public function handle($request, \Closure $next)
{
if ('testing' !== app()->environment())
{
return parent::handle($request, $next);
}
return $next($request);
}
source: https://laracasts.com/discuss/channels/testing/trouble-testing-http-verbs-with-phpunit-in-laravel/replies/29878
I am running laravel and i just implemented PHP Excel in order to export excels with data. For security reasons i want to redirect the user if his facebook id is no match with "my administrator facebook id".
My controller is named ExcelController and it has different functions inside it.
I do not want to use the below code inside every function so i am trying to find something to execute like __construct() - everytime this controller is accessed :
if(Auth::user()->facebook_id != env('facebook_id_admin_access')) {
return redirect()->route('home');
}
env('facebook_id_admin_access') is an .env variable(like a constant) with the facebook id i want to give access so it can use the specific controller.
I tried creating a public function __construct(){} in the controller and put the above code block but it doesn't get called.
Is this possible and how? Should i use middleware for that?
EDITED QUESTION
1) Created a middleware CheckAdminAccess
*/
public function handle($request, Closure $next)
{
if(Auth::user()->facebook_id != env('FACEBOOK_ID_ADMIN_ACCESS') || !Auth::check()) {
return redirect()->route('/');
}
return $next($request);
}
2) Updated in app\http\kernel.php to
protected $routeMiddleware = [
'admin_access' => 'App\Http\Middleware\CheckAdminAccess',
];
3) Updated in routes.php :
Route::get('/output/completed', 'ExcelController#completed')->middleware('admin_access');
But it doesn't seem to work, did i forgot anything?
After creating a middleware,
To make it work i changed in my routes.php from :
Route::get('/output/completed', 'ExcelController#completed')->middleware('admin_access');
To :
Route::group(['middleware' => ['auth','admin_access']], function() {
Route::get('/output/completed', 'ExcelController#completed');
});
I have an admin middlware AdminMiddleware.php
public function handle($request, Closure $next, $guard = null)
{
if(Auth::check())
{
if($request->user()->is_admin==1)
{
return $next($request);
}
return redirect('/login');
}
else
{
return redirect('/login');
}
}
And i have some routes under adminmiddleware:
Route::group(['middleware' => ['App\Http\Middleware\Adminmiddleware']], function () {
//admin routes
});
Working properly. And i'm litte confused on Registering middleware? Why should i register. Only for alias or something more effective?
For $routeMiddleware and $middlewareGroups, they are mostly for aliasing. As you mentioned, you can don't register it and use it just fine in your route file.
It's more on conveniency for your to swap the implementation shall you need to.
IMO, register it so that you can swap the middleware implementation at any point of time without going into your route configuration file to change it - which is more error prone than one central location to change.
Registering middleware is loading the class with each request automatically. You're telling Laravel to load and register it, so you could use it in routes and controllers without initializing it manually. Same with console commands, service providers etc. You must register before using them.
If you want a middleware to run during every HTTP request to your application, simply list the middleware class in the $middleware property of your app/Http/Kernel.php class.