Laravel: strange behavior when I try to serve ajax and non-ajax request on same route (Caching) - laravel

I registered a GET route for laravel.dev/test. The corresponding controller for the route would distinguish whether the request is ajax or not.
When I type laravel.dev/test on the browser, Laravel detect that it's not an ajax request and uses return View::make() to generate the page. Then Backbone.js code on the page make an ajax request to laravel.dev/test and Laravel uses return Response::json to return a JSON.
It's all fine until when I try to navigate away from the page and then use the browser button to navigate back to laravel.dev/test that it print out the json as the response, which is not what I expect since I'm not making an ajax request.

Definitely a caching issue. Just to try and get some results, add this to your controller (ajax and non-ajax) to force-disable caching:
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
And see if chrome still fetches from the cache on the back button.

This is not a laravel or backbone, but a chrome issue! Check this out too.
The solution that worked for me is to put
return Response::json($this->data)->header("Vary", "Accept");
Good luck!

This is in Laravel 5.1, but the principle should work for all previous versions as well.
The way I handled it was with two routes pointing to the same method, but with one ending in a .json extension:
get('items', ['as' => 'items', 'uses' => 'ItemsController#index']);
get('items.json', ['as' => 'items', 'uses' => 'ItemsController#index']);
Then inside my index() method:
$data = []; // your collection
if ($this->request->ajax()) {
return response()->json($data); // replace with actual JSON data
}
return view('items.index', compact('data'));
This allows for a dedicated URL for JSON responses, uses the same method and data, and never interferes with my back button.

Related

Stop Laravel 9 from redirecting if AJAX validation fails

I have a Laravel 9 application and I am using Vue 3 and Filepond to create an image uploader experience.
<template>
<file-pond
max-files="1"
name="images"
server="/api/images"
accepted-file-types="image/jpeg, image/png, image/gif, image/webp"
:files="file"
/>
</template>
<script setup>
import vueFilePond from 'vue-filepond'
import 'filepond/dist/filepond.min.css'
const file = ''
</script>
It is very straightforward, it's basically sending the uploaded file to the server at the API endpoint to '/api/images' which is the API endpoint of my Laravel application and it works as expected.
However when I try to validate the request in the Laravel controller, I always get a 302 redirect reponse back in my Vue 3 application. This is how my validator looks like:
public function upload(Request $request)
{
$request->validate(
[
'image' => 'required|image|dimensions:min_width=1,max_height=1|max:4000',
]
);
if ($request->fails())
{
dd('Request failed. App should stop here.');
}
}
I deliberately set the "max_height" to 1 so the validatior fails, for testing. However the app doesn't go to "Request failed.." it just simply redirects (302) the user instead of returning the errors.
I am assuming that Laravel doesn't recognize this as an AJAX request for some reason. Because these request validations always work perfect if I do a simple Vue Axios form submit. I always get the errors in JSON format. But here it's not the case.
I have tried doing this and it was successfully giving me the "Validation fails message":
$validator = Validator::make($request->all(),
[
'image' => 'required|image|dimensions:min_width=1,min_height=1|max:4000',
]);
if ($validator->fails())
{
dd('Validator fails.');
}
However I would want to use a Form Request validation here (so I can put the contents of the validation logic into it's spearate file.)
What I want is: when the image validation fails -> Laravel should return a JSON response with all the errors. Just like when a form validation fails.
What am I missing though? Why isn't it working like it's supposed to in the first situation?
You are mostly just missing the header accept = application/json
That header is the only real difference between an ajax call and a normal request regardless of the route containing "api" or not.
Since the request expects a json response, the exception handler will not send a redirection, it will instead return a json of the error. albeit a validation exception error response or any exception error.

Inertia - Reloading page url created with POST shows GET 405 (Method Not Allowed)

I want to load a new vue component and show its id in the URL. This can be done like so:
<template>
<button #click="submit">Load new page with id in url</button>
</template>
<script>
import {Inertia} from "#inertiajs/inertia";
const submit = () => {
const id = 1
Inertia.post('/admin/kreationen/bearbeiten/' + id, {id: id})
}
</script>
and on the laravel side (web.php):
Route::post('/admin/kreationen/bearbeiten/{id}', function (Request $request) {
return inertia('backend/cms-gallery-edit', ['id' => $request->id]);
});
Problem is, when I am reloading the page, this error is shown:
GET http://127.0.0.1:8000/admin/kreationen/bearbeiten/1 405 (Method Not Allowed)
I understand why this error is happening but how would I achieve the goal of showing the id in the new url without getting this error?
Edit:
I don't know why my original approach didn't work yesterday but now it does. For anyone interested:
<inertia-link :href="'/admin/kreationen/bearbeiten/' + data['id']">
<button>Load new page with id in url</button>
</inertia-link>
backend:
Route::get('/admin/kreationen/bearbeiten/{id}', function (Request $request) {
return inertia('backend/cms-gallery-edit', ['id' => $request->id]);
});
Probably you needed to use GET Request instead of POST Request. If yes, then just add to url your id and then pass the GET method to param:
const id = 1
Inertia.visit('/admin/kreationen/bearbeiten/' + id, {method: 'get'})
And for Backend side in Laravel url stays as it is, but recieved parameter is only $id and method needs to change to GET:
Route::get('/admin/kreationen/bearbeiten/{id}', function ($id) {
return inertia('backend/cms-gallery-edit', ['id' => $id]);
});
The 405 (Method Not Allowed) occurs when you try to load the POST methoded url via GET method and vice versa.
So first when you click, the post method is working fine, but when you reloading the page, it counts as GET method (just like direct opening the page). So recieving 405 error is fair, if you are able to use GET method to your needs, then use GET method (if you are ok with whenever user accesses the page directly without any form posted).
If not, then you might firstly do you POST requests in different route, then redirect user to another route (To prevent re-sending form when reloading the page, but to opening another page with some form data (ofcourse it must be saved somewhere like session or db))

Laravel: controller not teletransporting me (redirect-ing me) to the page

from Ajax the controller does get the keyword I want, as it confirms it (because I echo it), and my idea was that on getting that keyword, it should redirect to the page I want. Yet, it does not, and also, while it does change the locale, I have to reload the page, otherwise, it won't show any translation and locale changes on the page. In Firebug when I hover over the POST, I get the correct URL to where I would want to go: sort of http://myweb.com/es but the controller does not change the http URL box of my browser on my web to go there.
I am simplifying the Controller code here, but actually I will want it to go to different URLs depending on the keyword it gets, something that I would do with a switch statement or IF else if etc.
So the controller is as simple as this:
public function changelanguage()
{
$lang = \Input::get('locale');
echo "I got $lang";
Session::put('locale', $lang);
return redirect('/es');
}
If instead of using ajax I use a Form, then I dont need to reload, the Action of the form makes the controller change the locale and translate the page without reloading. But I need to use ajax and in any case, the controller does get correctly the keyword ('en', 'es', 'de' etc ) for languages, so it should take it from there and redirect me to the URL page, but it just doesnt move.
if you are curious about the Ajax, here it is, but it does send the keyword as I said.
$(document).ready(function(){
$('.choose-language').on('click', function(e){
e.preventDefault();
var selectedlanguage = $(this).data('value');
$.ajax({ // so I want to send it to the controller
type:"POST", // via post
url: 'language',
data:{'locale': selectedlanguage},
}); // HERE FINISHES THE $.POST STUFF
}); //HERE FINISHES THE CLICK FUNCTION
}); // HERE FINISHES THE CODE
ROUTES
Route::post('language', array(
'as' =>'language',
'uses' => 'LanguageController#changelanguage'
));
If you’re trying to perform the redirect in the AJAX-requested script, then it won’t work. You can’t redirect from a script request via AJAX otherwise people would be doing all kinds of nefarious redirects.
Instead, set up a “success” handler on your AJAX request that refreshes your page if the request was successful. It can be as simple as:
var url = '/language';
var data = {
locale: $(this).data('value');
};
var request = $.post(url, data)
.success(function (response) {
// Script was successful; reload page
location.reload();
});
I’m not sure how you’re allowing users to select locales, but since you need a reload any way I think AJAX is pointless here. Just have a traditional form that submits the new locale to an action, set the locale in a session/cookie/whatever, and then redirect back to the referring page.

Laravel 4 get previous url on filter

I have form, which the post action will goes to ajax filter (only permit ajax post). If its not ajax, I want the page will be redirected to the submitted form page (previous page). URL::previous just result undefined.
Route::filter('ajax', function() {
if (Request::ajax() === false)
//URL::previous()
});
I use laravel 4.2
I wanted to place a comment but I don't have enough rep.
Did you try the following option?
Redirect::back()->withErrors('error', 'Not a ajax post');
Or without a message
Redirect::back();

JavaScript code in view issue in Laravel

I put JavaScript code in a view file name product/js.blade.php, and include it in another view like
{{ HTML::script('product.js') }}
I did it because I want to do something in JavaScript with Laravel function, for example
var $path = '{{ URL::action("CartController#postAjax") }}';
Actually everything is work, but browser throw a warning message, I want to ask how to fix it if possible.
Resource interpreted as Script but transferred with MIME type text/html
Firstly, putting your Javascript code in a Blade view is risky. Javascript might contain strings by accident that are also Blade syntax and you definitely don't want that to be interpreted.
Secondly, this is also the reason for the browser warning message you get:
Laravel thinks your Javascript is a normal webpage, because you've put it into a Blade view, and therefore it's sent with this header...
Content-Type: text/html
If you name your file product.js and instead of putting it in your view folder you drop it into your javascript asset folder, it will have the correct header:
Content-Type: application/javascript
.. and the warning message will be gone.
EDIT:
If you want to pass values to Javascript from Laravel, use this approach:
Insert this into your view:
<script type="text/javascript">
var myPath = '{{ URL::action("CartController#postAjax") }}';
</script>
And then use the variable in your external script.
Just make sure that CartController#postAjax returns the content type of javascript and you should be good to go. Something like this:
#CartController.php
protected function postAjax() {
....
$contents = a whole bunch of javascript code;
$response = Response::make($contents, '200');
$response->header('Content-Type', 'application/javascript');
....
}
I'm not sure if this is what you're asking for, but here is a way to map ajax requests to laravel controller methods pretty easily, without having to mix up your scripts, which is usually not the best way to do things.
I use these kinds of calls to load views via ajax into a dashboard app.The code looks something like this.
AJAX REQUEST (using jquery, but anything you use to send ajax will work)
$.ajax({
//send post ajax request to laravel
type:'post',
//no need for a full URL. Also note that /ajax/ can be /anything/.
url: '/ajax/get-contact-form',
//let's send some data over too.
data: ajaxdata,
//our laravel view is going to come in as html
dataType:'html'
}).done(function(data){
//clear out any html where the form is going to appear, then append the new view.
$('.dashboard-right').empty().append(data);
});
LARAVEL ROUTES.PHP
Route::post('/ajax/get-contact-form', 'YourController#method_you_want');
CONTROLLER
public function method_you_want(){
if (Request::ajax())
{
$data = Input::get('ajaxdata');
return View::make('forms.contact')->with('data', $data);
}
I hope this helps you... This controller method just calls a view, but you can use the same method to access any controller function you might need.
This method returns no errors, and is generally much less risky than putting JS in your views, which are really meant more for page layouts and not any heavy scripting / calculation.
public function getWebServices() {
$content = View::make("_javascript.webService", $data);
return (new Response($content, 200))->header('Content-Type', "text/javascript");
}
return the above in a method of your controller
and write your javascript code in your webService view inside _javascript folder.
Instead of loading get datas via ajax, I create js blade with that specific data and base64_encode it, then in my js code, I decode and use it.

Resources