I've created a terminable middleware that sends a request to Google Analytics. One of the attributes I send is the server response time. Here's how I do it:
In \App\Http\Kernel I add the SendAnalytics middleware:
class Kernel extends HttpKernel {
...
protected $middleware = [
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
...
'App\Http\Middleware\SendAnalytics',
];
}
And the SendAnalytics middleware looks like:
class SendAnalytics implements TerminableMiddleware {
protected $startTime;
public function __construct() {
$this->startTime = microtime(true);
}
public function handle($request, Closure $next) {
return $next($request);
}
public function terminate($request, $response) {
$responseTime = microtime(true) - $this->startTime;
/* I send a request to Google here, using their Measurement Protocol */
// Dying for debugging purposes
dd($responseTime); // Always prints 0.0
}
}
But this always shows 0.0. What would be the correct way to show the server response time?
I used microtime(true) - LARAVEL_START. Seems to give a fairly accurate response time.
As Bogdan mentions in the comment:
The LARAVEL_START constant is defined in bootstrap/autoload.php which
is the very first file included from public/index.php, so this makes
it the first statement to be executed. If you place the middleware
last on the list, its terminate method will be the last one executed
before app->terminate() will be called, so you should get a pretty
good computation of execution time.
I noticed that in a single request life cycle middleware instance may be initialized more than once. The second time is right before terminate call. That would explain zero time result (on my machine it was not zero but pretty close to it, while the actual request time was more like 200ms). The handle method was obviously called only once and this is where start time must be recorded.
class SendAnalytics implements TerminableMiddleware {
protected $startTime;
public function handle($request, Closure $next) {
$this->startTime = microtime(true);
return $next($request);
}
...
}
Related
I have the following test cases:
protected function setUp(): void
{
parent::setUp();
Queue::fake();
}
public function testUpdate(): void
{
$this->updateModel(["foo" => 123]);
$this->assertDatabaseHas("models", ["foo" => 123]);
}
public function testQueueJob(): void
{
$this->updateModel(["bar" => 456]);
$this->assertDatabaseHas("models", ["bar" => 456]);
$this->assertPushed(MyJob::class);
}
public function testDontQueueJob(): void
{
$this->updateModel(["baz" => 789]);
$this->assertDatabaseHas("models", ["baz" => 789]);
$this->assertNotPushed(MyJob::class);
}
The updateModel just pushes out a post request to the controller. The request is handled by this method:
public method update(Request $request, Model $model): JsonResponse
{
$model->update($request->all());
$model->save();
if ($this->wasChanged("bar")) {
MyJob::dispatch($model);
}
return response()->json($model);
}
So obviously, what I'm testing is that the job doesn't get pushed onto the queue. However, my final test is failing with:
The unexpected [App\Jobs\MyJob] job was pushed.
Failed asserting that actual size 1 matches expected size 0.
I have confirmed with dump() statements that the job is not being pushed during the third test. By swapping the second and third tests, the test suite passes successfully, suggesting that the job pushed onto the queue in the second test has persisted to the third test.
I am using the database queue driver (QUEUE_CONNECTION=database in .env.testing) and the RefreshDatabase trait is imported into my test class. This should erase the database between tests but it seems that it is not.
When I try clearing the queue manually, I get:
Call to undefined method Illuminate\Support\Testing\Fakes\QueueFake::clear()
Is there some way to clear the queue in the setUp method? Is there a different way I should be handling this?
I think you should use faker
public function testDontQueueJob(): void
{
$newNumber = $this->faker->numberBetween(100,999);
$this->updateModel(["baz" => $newNumber]);
$this->assertDatabaseHas("models", ["baz" => $newNumber]);
$this->assertNotPushed(MyJob::class);
}
to be sure that this conditional is working
if ($this->wasChanged("bar")) {
MyJob::dispatch($model);
}
Order of tests shouldn't affect the result
Also I noticed, that you use key "baz" in test and "bar" in the controller, is it correct?
Since that (important) part is missing, lets make sure you're doing both:
[...] and the RefreshDatabase trait is imported into my test class.
Are you only importing it or also using it? Your TestClass needs to look something like this:
<?php
[..]
use Illuminate\Foundation\Testing\RefreshDatabase;
[..]
class YourTest extends TestCase
{
use RefreshDatabase;
[..]
}
When I extends ApiBaseController in another class, response token denied is doesn't work. even though I put wrong app-token but still give response in another class.
class ApiBaseController extends Controller
{
protected $user;
public function __construct()
{
if (request()->header('app-token') != 'ofdsafalkhguddskjafl01JhBF9mGx2jay'){
return response()->json([
'success'=>false,
'status'=>'401',
'message'=>'Token Denied !',
'response'=>[
'total'=>0,
'data'=>[]
]
]);
}
else{
$this->user = Auth::guard('api')->user();
}
}
}
This class still work even though I put wrong app-token
class AttendeesApiController extends ApiBaseController
{
public function index(Request $request)
{
return Attendee::scope($this->account_id)->paginate($request->get('per_page', 25));
}
}
I want to make sure when app-token is wrong will give Token Denied ! response
please give me some advice
You will have to call parent constructor to make this work.
class AttendeesApiController extends ApiBaseController{
function __construct(){
parent::__construct();
}
public function index(Request $request){
return Attendee::scope($this->account_id)->paginate($request->get('per_page', 25));
}
}
If I am not mistaken, you will also have to put a kind of a die in the constructor to avoid further execution.
Update:
Best way to handle this is to group these routes inside a middleware and have the bearer token check in the middleware itself. This will make your approach more neat and you can easily add new routes that require bearer token check in this route middleware group.
While it is a good idea to keep the token validation concern separated, it is not a good practice to do such thing in the constructor, let alone hide it in the constructor of a base class.
In general, constructors should be used to construct the object, not to "do things".
Because you want to return early, it gets a bit complicated to extract this concern out of the controller. But that's what middleware is for.
Take a look at the Laravel documentation on creating your own middleware (altough what you are trying to do might be already built in)
An example middleware class could look like this:
<?php
namespace App\Http\Middleware;
use Closure;
class CheckToken
{
/**
* Handle an incoming request and check the token.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (...) { //your token check
return ...; // your early-returned json.
}
return $next($request); //otherwise continue
}
}
I'm doing an existence check within a middleware, by checking a route-parameter.
If the check succeeds, I'm attaching it's model to the request to make it available throughout the rest of the request-cycle, application.
// App\Http\Middleware\CheckForExistence.php:
...
public function handle($request, Closure $next)
{
// some checks...
// success
$request->attributes->add([
'company' => $someModel
]);
}
I now have a controller which 'needs' this information in a couple of methods. So my thought was to add it to the construct of the controller and add it as a protected var in the whole controller:
// App\Http\Controllers\MyController.php
<?php
use Illuminate\Http\Request;
class MyController extends Controller
{
protected $company;
public function __construct(Request $request)
{
$this->company = $request->attributes->get('company');
}
public function index()
{
dd($this->company); // returns null
}
}
This controllers index() returns null instead of the give model.
If I change the index() method to:
public function index(Request $request)
{
return $request->attributes->get('company');
}
This returns the model; as expected.
Why is this happening? It looks like the middleware is not run when the controller is constructed.... Is there a way to circumvent it?
Or am I missing the obvious here.....
I could off course repeat myself in each method; but that is not very DRY ;)
You can't access the session or authenticated user in your controller's constructor because the middleware has not run yet, So you can do it like this :
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->company = $request->attributes->get('company');
return $next($request);
});
}
For reasons currently unclear to me, the controller object is constructed before the request changes are reflected in the request object. In short the request is not considered properly constructed when a controller is constructed. This post seems to imply that.
There's two ways to work around this (if for a second we ignore what you're trying to do).
Use request dependency injection
public function index(Request $request)
{
$compary = $request->attributes->get('company');
}
This is not really WET because you're just swapping $this->company with $request->attributes->get('company') it's just a refactor. You should be injecting the request in the controller action anyway and if you don't want to do that you can use the request() helper.
Use a callback middleware in the constructor (Maraboc's answer explains how)
Now if you want a more case specific solution though you can use case specific dependency injection:
If you need to bind a model to a specific route parameter you can use route model binding and add the following in your RouteServiceProvider (or any provider).
Route::bind("companyAsARouteVarName", function () {
// this is why more details in the question are invaluable. I don't know if this is the right way for you.
//checks
// success
return $someModel;
});
Then you will register your route as:
Route::get("/something/{companyAsARouteVarName}", "SomeController#index");
and your controller will be:
public function index(Company $companyAsARouteVarName) {
//Magic
}
Controller constructor will be initialized before middleware execution.
You can get data from Injected $request object in controller functions.
I decided to implement my own small framework to implement such stuff like dependency injection etc.
Now I'm stucking at my middleware implementation. I can add middleware to a route but I im wondering how slim loops through the attached middleware.
I'd like to do it the slim way, so in every middleware I can return a request or a response or the next middleware. But how do I have too iterate over my attached middleware.
Here is my stack I want to proceed
class MiddlewareStack
{
private $stack;
public function addMiddleware(Middleware $middleware)
{
$this->stack[] = $middleware;
}
public function processMiddleware(Request $request, Response $response)
{
}
}
and thats the middleware interface
public function __invoke(Request $request, Response $response, $next);
I want to
return $next($request,$response);
in my middleware classes or just a response or a request.
Here's how to create middlware callable in slim.
http://www.slimframework.com/docs/concepts/middleware.html#invokable-class-middleware-example
Slim 3 first adds itself to the stack which is the Slim\App#__invoke() which executes the route.
Then, when you add a middleware it does the following: (before this slim wrapps the callable (annonymous function/invokable class) inside a DeferredCallable which helps to execute both the function and class equally (See Slim\App#add()).
protected function addMiddleware(callable $callable) // $callable is a DeferredCallable
{
$next = $this->stack->top(); // when it the first middleware this would be the route execution
$this->stack[] = function (ServerRequestInterface $req, ResponseInterface $res) use ($callable, $next) {
$result = call_user_func($callable, $req, $res, $next);
return $result;
};
}
(This is only the simple code, for full code see: Slim\MiddlewareAwareTrait#addMiddleware())
So the middleware which is on top of the stack executes the other middleware as well, because it is provided in the next method.
Then when you want to execute the middleware, get the middleware which is on top of the stack and execute it.
$start = $this->stack->top();
$resp = $start($req, $res);
// $resp is now the final response.
(see Slim\MiddlewareAwareTrait#callMiddlewareStack())
I am creating a public API using Laravel 5.2. To keep the code in the controller short, I have created several helper functions, one of which is a function to return a response to the API call, as shown below:
if (! function_exists('buildResponse'))
{
function buildResponse($data)
{
return response()->json($data)->header('foo', 'bar');
}
}
This is how I would call it from the controller:
public function registration()
{
buildResponse(['a' => 'Success']);
}
However this does not work. It only works if I add a return in front of the function.
public function registration()
{
return buildResponse(['a' => 'Success']);
}
One might say, "Okay, then just include the return!". The thing is, this particular function will be called by another helper function, for example:
if (! function_exists('throwResponseCode'))
{
/**
* Throws a response code to the client.
* #param $code
* #return mixed
*/
function throwResponseCode($code)
{
switch ($code) {
case '101':
$data[$code] = 'Unable to establish a database connection';
return buildResponse($data);
// Other cases here
}
}
}
Is there any way that I can send a response to the client from the helper function?
Keep it simple bro!
Change
return response()->json($data)->header('foo', 'bar');
To
return response()->json($data)->header('foo', 'bar')->send();
Short answer, no. Not directly.
The purpose of return is to finish the function and give results. Laravel considers the return value of controller to be the response, so other functions can not do this using return.
There are someways around this. Consider this. Create a middleware, that works after the response has been generated by controller, i.e. calls $next($request) in the first line. Here you can change the response as you wish. To connect helpers to this middleware, you can use static methods or properties. The logic of middleware would be that if someone has put something in, say, $response static property of middleware, then send this one instead of controller response.