How to stop php script in Laravel when Axios request is cancelled? - php

I'm making a live search using Laravel, VueJs and axios, so every time the user types a new word, the previous request will be cancelled. My problem is that even when I cancel the previous request using the cancel token (https://github.com/axios/axios#cancellation), the php script is still running.
QUESTION: How can i stop the php script if the axios request has been cancelled?
my Vuejs code
fetchData(query) {
if(cancel != undefined)
cancel(); // cancel the previous request
axios.get("http://sample-link", {
cancelToken: new CancelToken(function executor(c) {
cancel = c;
}),
params: {
query : query
}
}).then(response => {
console.log(response);
}).catch(error => {console.log(error.message)})
}
...
my php code
class SearchController extends Controller {
public function Search(Request $request)
{
$query = $request->input('query');
$accounts = Accounts::search($query, null, true, true)->get();
return response()->json($accounts);
}
...
}

I don't know exactly how the Axios cancellation works, but if the client disconnects the HTTP session (as it should) you could try using the ignore_user_abort setting.
public function Search(Request $request)
{
ignore_user_abort(false);
$query = $request->input('query');
$accounts = Accounts::search($query, null, true, true)->get();
return response()->json($accounts);
}
Other useful functions could be:
connection_status: Check the current status of the connection
connection_aborted: Check if the connection has been canceled
I just want to add that your function does not seem very intensive, so I'm not sure how useful such a check is. As Tauqeer suggested in the comment, the usual approach in those situations is to apply a debounce to your javascript search function, in order to only fire the request when the user finished typing.
An example using lodash:
const search = (query) => {
.. your axios request ...
}
const fetchData = _.debounce(search, 300)
Now you can just call fetchData(query) whenever the user types something and it will only send a request when the user stops typing for 300 milliseconds

Related

Laravel testing. Get rout autoredirects from "/signin" to "am/signin", when for post I need to manually write "am/signin". Why post acts like this?

I use Laravel 9, but it is updated version. I mean, the project was written in laravel 5.4 and updated it to 9 later.
Here's my routes
Route::group(['middleware' => 'locale', 'prefix' => '{locale?}'], function () {
Route::get('/signin', 'Front\UsersController#getSignin');
Route::post('/signin', 'Front\UsersController#postSignin');
});
Here's my tests
public function test_get_signin(){
$response = $this->get('/signin');
$response->assertStatus(302);
}
public function test_post_signin(){
$response = $this->post('am/signin');
$response->assertStatus(302);
}
It works well when I do $this->post('am/signin');
But it is not correct. By the idea it must work when I write $this->post('/signin') without manually adding what should be added automatically. But in fact I get status code 405.
UsersController method for post('/signin) route.
public function postSignin($lang, Request $request){
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if (Auth::validate(['email' => $request['email'],'password' => $request['password']])
|| Auth::validate(['username' => $request['email'],'password' => $request['password']])) {
$verify = User::where(['email'=> $request['email']])->first();
$verify = $verify?$verify:User::where(['username'=> $request['email']])->first();
if($verify->status =='blocked'){
if ($request->expectsJson()) {
return response()->json(array('verify'=> trans('email.profile-block')), 422);
}
}elseif(!$verify->verified){
return response()->json(array('verify'=> trans('validation.active_account').' '
.trans('car.click').' <a class="resend-link" href="/'.$lang.'/resendtoken/'.$verify->email_token
.'">'.trans('car.here1').'</a> '.trans('validation.resend_link')), 422);
}elseif($verify->status =='pending'){
return response()->json(array('verify'=> trans('car.company_napp')), 422);
}elseif($verify->status =='active'){
if(filter_var($request->email, FILTER_VALIDATE_EMAIL)){
$field = 'email';
}else{
$field = 'username';
}
if (Auth::attempt([$field => $request->email, 'password'=>$request->password],$request->remember)) {
UserLoginInfo::create(['user_id'=>Auth::id(),'ip_address'=> $request->ip(),
'info'=>json_encode(self::get_user_info())]);
if ($request->remember) {
User::where('id', Auth::id())->update(array('signed_in_for_remember_me' => Carbon::now()));
}
return Auth::user()->balance;
}
}
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
more short:
$this->get('/signin'); gives status code 302
$this->post('am/signin'); gives status code 302
$this->post('/signin'); gives status code 405
I tried
to use withoutMiddleware() - no positive result.
public function test_get_signin(){
$response = $this->withoutMiddleware('locale')->get('/signin');
$response->assertStatus(302);
}
public function test_post_signin(){
$response = $this->withoutMiddleware('locale')->post('/signin');
$response->assertStatus(302);
}
removed "?" symbol from 'prefix' => '{locale?}' in route group - no use.
commented the : Route::get('/signin', 'Front\UsersController#getSignin'); - no use again.
instead of withoutMiddleware('locale') I also used just a withoutMiddleware() - no use again.
IMPORTANT!
Both routes work well when testing with browser. I mean. User can enter his signin page and also successfully be signed in.
What's going on? How can I force post to work automatically with "/signin"? It's also weird, if I manually have to write "am/" then why it returns 302 instead of 200?
Update
I created a new project and tried to experiment there with route group and get, post methods.
Here's the routes
Route::group(['middleware' => 'locale', 'prefix' => '{locale?}'], function () {
Route::get('/test', function () {
return view('welcome');
});
Route::post('/test', function () {
return view('welcome');
});
});
here's the tests
public function test_1()
{
$response = $this->get('/test');
$response->assertStatus(200);
}
public function test_2()
{
$response = $this->post('/test');
$response->assertStatus(200);
}
test1 and test2, both of them return status 404
And when I use tests like this, I mean, adding a prefix manually
public function test_1()
{
$response = $this->get('/am/test');
$response->assertStatus(200);
}
public function test_2()
{
$response = $this->post('/am/test');
$response->assertStatus(200);
}
both of them return 200
This is good, very good. But why in my situation of my current project I get for get method 200 instead of 404 and for post 405 instead of 404, I don't have an idea. The interesting fact. I commented the whole exceptions Handler.php's code and no use. The get method everytime returns 200 when it must return 404.
Update 2
I've got some new interesting info from my experiments. If in my current project I want to test get method with uri "/signin" without '/am' part with $this->withoutMiddleware(); then I get 500, but without $this->withoutMiddleware(); I get 200
And to know what 500 want to say I used $response->dd(); and got this result
Spatie\LaravelIgnition\Exceptions\ViewException: Undefined variable
$errors in file
C:\xampp\htdocs\dashboard\test\hayvcar\storage\framework\views\7ced869fcb986989ef0f5838d6567d1b5fa4f895.php
on line 1
And if use $this->withoutExceptionHandling(); instead of $response->dd();
We'll get this result
Illuminate\View\ViewException : Undefined variable $errors (View: C:\xampp\htdocs\dashboard\test\hayvcar\resources\views\front\transport\index.blade.php)
C:\xampp\htdocs\dashboard\test\hayvcar\storage\framework\views\7ced869fcb986989ef0f5838d6567d1b5fa4f895.php:1
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\View\Engines\PhpEngine.php:60
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\View\Engines\CompilerEngine.php:61
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\View\View.php:139
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\View\View.php:122
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\View\View.php:91
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Http\Response.php:69
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Http\Response.php:35
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Routing\Router.php:833
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Routing\Router.php:802
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Routing\Router.php:725
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:141
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:116
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Routing\Router.php:726
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Routing\Router.php:703
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Routing\Router.php:667
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Routing\Router.php:656
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php:167
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:141
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:116
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php:142
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php:111
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Concerns\MakesHttpRequests.php:526
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Concerns\MakesHttpRequests.php:293
C:\xampp\htdocs\dashboard\test\hayvcar\tests\Feature\RoutesTesting.php:1718
Caused by
ErrorException: Undefined variable $errors
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Foundation\Bootstrap\HandleExceptions.php:255
C:\xampp\htdocs\dashboard\test\hayvcar\storage\framework\views\7ced869fcb986989ef0f5838d6567d1b5fa4f895.php:1
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Filesystem\Filesystem.php:107
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Filesystem\Filesystem.php:108
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\View\Engines\PhpEngine.php:58
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\View\Engines\CompilerEngine.php:61
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\View\View.php:139
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\View\View.php:122
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\View\View.php:91
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Http\Response.php:69
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Http\Response.php:35
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Routing\Router.php:833
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Routing\Router.php:802
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Routing\Router.php:725
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:141
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:116
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Routing\Router.php:726
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Routing\Router.php:703
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Routing\Router.php:667
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Routing\Router.php:656
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php:167
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:141
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:116
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php:142
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php:111
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Concerns\MakesHttpRequests.php:526
C:\xampp\htdocs\dashboard\test\hayvcar\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Concerns\MakesHttpRequests.php:293
C:\xampp\htdocs\dashboard\test\hayvcar\tests\Feature\RoutesTesting.php:1718
Have you tried delete cached files?
Delete everything in bootstrap\cache and try again on host.
I've explained the whole situation here, for another question, which in this case, it doesn't matter if it's testing or using in browser, when you cache your routes, laravel look for the cached file, instead of defined routes in web.php and api.php.
So if in cached file, you've had route with am/signin then, doesn't matter if you change it to signin or not, in web.php/api.php, it always looking for cached file, which in this case is am/signin.
Temporary questions, answer them by question number :
So you've commented everything in postSignin method, and didn't worked or just a part of it?
You said in browser is working, method is getting $lang from route? because in tests, as far as i can see, you're not passing anything.
Have you tried to change post to patch just for test? (that's because of Patch verb)
You might also try withoutExceptionHandling(); to get more details on error and test output. are you posting full test or just a basic test?
You can add this line to top of your test, $this->withoutExceptionHandling(); and get more details.
Remove $lang from method or just try a new method like below:
public function postSignin(Request $request){
return something or return $this->sendFailedLoginResponse($request); or etc.
}
How did you upgraded your project to Laravel 9? Create a new Laravel project and only test these two routes, get and post.

Route Middleware in Slim 4 doesn't stop invoking the callable in the route

I'm strugling with authorization middleware in Slim4. Here's my code:
$app = AppFactory::create();
$app->add(new Authentication());
$app->group('/providers', function(RouteCollectorProxy $group){
$group->get('/', 'Project\Controller\ProviderController:get');
})->add(new SuperuserAuthorization());
Authentication middleware checks the user and works fine.
The method get in ProviderController is
public function get(Request $request, Response $response): Response{
$payload = [];
foreach(Provider::all() as $provider){
$payload[] = [
'id' => $provider->id,
'name' => $provider->name,
];
}
$response->getBody()->write(json_encode($payload));
return $response;
}
The SuperuserAuthorization looks like this
class SuperuserAuthorization{
public function __invoke(Request $request, RequestHandler $handler): Response{
$response = $handler->handle($request);
$authorization = explode(" ", $request->getHeader('Authorization')[0]);
$user = User::getUserByApiKey($authorization[1]);
if(! Role::isSuperuser($user)){
return $response->withStatus(403);//Forbidden
}
return $response;
}
}
The thing is that even though the user is not a superuser, the application continues executing. As a result I get json with all the providers and http code 403 :/
Shouldn't route middleware stop the request from getting into the app and just return 403 right away?
I know that I can create new empty response with status 403, so the data won't come out, but the point is that the request should never get beyond this middleware, am I right or did I just misunderstand something hereā€¦
Any help will be appreciated :)
------------- SOLUTION ----------------
Thanks to #Nima I solved it. The updated version of middleware is:
class SuperuserAuthorization{
public function __invoke(Request $request, RequestHandler $handler): Response{
$authorization = explode(" ", $request->getHeader('Authorization')[0]);
$user = User::getUserByApiKey($authorization[1]);
if(! Role::isSuperuser($user)){
$response = new Response();
return $response->withStatus(403);//Forbidden
}
return $handler->handle($request);
}
}
Shouldn't route middleware stop the request from getting into the app and just return 403 right away?
Slim 4 uses PSR-15 compatible middlewares. There is good example of how to implement an authorization middleware in PSR-15 meta document. You need to avoid calling $handler->handle($request) if you don't want the request to be processed any further.
As you can see in the example, if the request is not authorized, a response different from the return value of $handler->handle($request) is returned. This means your point saying:
I know that I can create new empty response with status 403, so the data won't come out, but the point is that the request should never get beyond this middleware
is somehow correct, but you should prevent the request from going further by returning appropriate response before invoking the handler, or throwing an exception and let the error handler handle it.
Here is a simple middleware that randomly authorizes some of requests and throws an exception for others:
$app->group('/protected', function($group){
$group->get('/', function($request, $response){
$response->getBody()->write('Some protected response...');
return $response;
});
})->add(function($request, $handler){
// randomly authorize/reject requests
if(rand() % 2) {
// Instead of throwing an exception, you can return an appropriate response
throw new \Slim\Exception\HttpForbiddenException($request);
}
$response = $handler->handle($request);
$response->getBody()->write('(this request was authorized by the middleware)');
return $response;
});
To see different responses, please visit /protected/ path a few times (remember the middleware acts randomly)

Symfony JsonResponse ignoring defined HTTP code and only responses 200

I'm trying change HTTP code in Response but I always receive 200.
the code below is resumed a lot with both tests:
$app->put('/hdc/v1/pagamento/{pagamento_id}', function($pagamento_id, Request $request) use ($app) {
$pagamento = Pagamento::find($pagamento_id);
return new JsonResponse(["message"=>"error"],400); // returns 200
}
$app->put('/hdc/v1/pagamento/{pagamento_id}', function($pagamento_id, Request $request) use ($app) {
// $pagamento = Pagamento::find($pagamento_id);
return new JsonResponse(["message"=>"error"],400); // returns 400
}
Interacting with the model, JsonResponse is impacted. Why?
Postman screenshot
I think you don't use the good method. See the silex doc :
<?php
$app->put('/hdc/v1/pagamento/{pag_id}', function($pag_id, Request $request) use ($app) {
return $app->json(array('message' => 'error'), 400);
});
Awkward!
My entity file Pagamento had
?>
in the file bottom.
it was sending 200 code header when the Pagamento::find method was called.

Redirecting back to web page after User has clicked cancel

I integrated facebook login through laravel/socialite and its working fine.
The problem is when user clicks cancel in the popup from facebook, the screen will stayed on facebook's page itself instead of redirecting to my site.
Is anywhere i need to specify the cancel redirecting url in facebook login app? I don't know how to handle this redirection.
Just found out the answer here
Just for other who are wondering, I just figured out a way... In
SocialAuthController: use Illuminate\Http\Request;
Then make constructor as:
protected $request;
public function __construct (Request $request) {
$this->request = $request;
}
and then in callback() function:
if (isset($this->request['error'])) {
return redirect()->to('/error');
//make your error route or do whatever you want!!
} else {
// this is my code, your actual code will go here on success
print_r($service);
$service = new SocialAccountService();
$user = $service->createOrGetUser(Socialite::driver($provider));
$providerUser = \Socialite::driver('facebook')->user();
print_r($providerUser);
auth()->login($user);
return redirect()->to('/home');
}

Backbone and Laravel API

A couple of questions that I cannot seem to find definative answers for, I am working on a RESTful api at the moment, that in time will interact with a handful of client devices, for the time being I am concetrating, on the API, and the web application that goes along with it.
The api lives at http://api.local and the web application lives at http://webapplication.local. The web application is a Laravel installation with a Backbone front-end.
I am trying to save some data from the Backbone to the database of the API. So the API save method looks like this,
public function store()
{
$user_details = Input::all();
$user = new User;
$user->firstname = $user_details['firstname'];
$user->surname = $user_details['surname'];
$user->email = $user_details['email'];
$user->password = Hash::make($user_details['password']);
$user->remember_token = '';
$user->save();
return Response::json($user_details, 200);
}
And it has a route that looks like this,
Route::group(['prefix' => 'api/v1'], function(){
Route::get('users', 'UsersController#index');
Route::get('users/{id}', 'UsersController#get');
Route::post('users/create', 'UsersController#store');
/**
* PUT request to edit the database record
* #todo: Need to be a PUT request in the long run
*/
Route::post('users/update/{id}', 'UsersController#update');
/**
* DELETE request to edit the database record
* #todo: Need to be a DELETE request in the long run
*/
Route::post('users/delete/{id}', 'UsersController#delete');
});
On the front end side (web application) I am saving a model like this,
saveNewUser: function(e) {
e.preventDefault()
var data = this.$el.find('.js-create-new-user').serializeJSON();
// create a new model
var User = new app.User();
User.save(data, {
success: function(model, response){
console.log(model);
console.log(response);
console.log("sucess");
},
error: function(model, response) {
console.log(model);
},
wait:true
});
}
This sends a POST request to the webapplication.local/users/create method, that looks like this,
public function create()
{
$data = Input::all();
$curl = new Curl\Curl();
$curl->post('http://pops.local/api/v1/users/create', $data);
if ($curl->error) {
return Response::json($curl->error, $curl->error_code);
}
else {
return Response::json($curl->response, 200);
}
}
If I look at the $curl->response then I see the data object that is being sent - which I assume means the cURL request is successfully being sent. However nothing gets updated in the API's database?
Am I going about this all wrong? Should Backbone be sending/making requests directly to the API, and leave the PHP behind the web application to do things that the API does not do i.e resize images etc?
You may send requests directly from backbone but you dont have to. Everything looks fine. Check what is returned from $user->save(); by writing something like that:
dd($user->save());
You should get true if everyting is fine, false if there is any error.

Categories