Stop Laravel 9 from redirecting if AJAX validation fails - ajax

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.

Related

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))

Inertia.JS reload props after post request

I am currently pretty confused why Inertia.js is not reloading or rerendering my page.
I have a form that could be filled, once submitting this will execute:
Inertia.post("/ban", ban);
This will redirect to a POST in the web.php of my Laravel.
That looks like this:
Route::post("/ban", [Speler::class, "ban"]);
This redirects to a class called "Speler", in there is a function called "ban".
Once done some SQL statements it will execute this return:
return Redirect::route("speler", [$steam])->with("success", "Ban doorgevoerd!");
This will go back to the web.php, and go to the route speler with the session data "success".
That redirect goes into the web.php:
Route::get("/speler/{steam}", [PageLoader::class, "speler"])->name("speler");
This one goes to the PageLoader controller, and execute the "speler" function.
But here it gets weird, this will return a Inertia::render of the same page the person was on, but would need to add another prop, or change the prop. (so i can show a popup with te text that it succeeded)
return Inertia::render("Panel/Speler",
["steamNaam" => $steamNaam, "discord" => $discord, "karakterAantal" => $karakterAantal, "karakters" => $karakters, "sancties" => $sanctiesReturn, "punten" => $aantalPunten, "steamid" => $steamid, "success" => $melding]
);
The "success" prop contains $melding that simply contains a string.
But for some reason it doesn't seem to re-render the page, the form stays filled, and the props are the same.
Does someone have a solution?
If you setup your form with the form helper, you can reset the form on a successful post like this:
form.post('/ban', {
preserveScroll: true,
onSuccess: () => form.reset(), // reset form if everything went OK
})
The success message, which you set with
return Redirect::route("speler", [$steam])->with("success", "Ban doorgevoerd!");
is usually taken care of by the Inertia middleware and made available as 'Shared data'. See this docs page on how to configure that. Shared data is rendered in $page.props variable. That is where you can find the Ban doorgevoerd! message. You can show these to the user as follows:
<div v-if="$page.props.flash.message" class="alert">
{{ $page.props.flash.message }}
</div>
Depending on your implementation, you would need to watch this property. See the FlashMessages component on the pingCRM demo app for an example implementation.

Laravel redirecting problem when submitting form

I have following routes
Route::get('videos/{video}/edit', 'VideoController#edit');
Route::put('videos/{video}/update2', 'VideoController#update2');
first route loads the following stripped view
<form action='/videos/{{$video->uid}}/update2' method='post'>
<button class='btn btn-default' type='submit'>Update</button>
{{csrf_field()}}
{{method_field('PUT')}}
</form>
from the controller code listed below
class VideoController extends Controller{
public function edit(\App\Models\Video $video){
return view('video.edit',[
'video' => $video,
]);
}
public function update2(VideoUpdateRequest $request,\App\Models\Video $video){
echo "ok";
}
}
according to this code, expected behavior should be to see "ok", instead of that I get HTTP 302 Redirect as shown below in Dev Console.
This is a weird behavior, which is not expected. How to get the expected behavior of displaying "OK" after submitting the form? How to debug this?
SOLVED
Problem was with HTML elements in the form are not having 'name' attributes thus Laravel Form Request Validation redirects back. after adding those missing attributes, form works as expected.
SOLVED
Problem was with HTML elements in the form are not having 'name' attributes thus Laravel Form Request Validation redirects back. after adding those missing attributes, form works as expected.

How do I redirect to my form after a Laravel failed form validation

When there is a failed validation , my form page was redirected to 'localhost' which is the php info page instead of form page to be displayed again. I don't know what's wrong. P. S I'm working with laravel 5.2.35
Try this
return redirect()->back()->with('failAuth', true);
At the page where your form is, you can add this line
<script>
var failAuth = "{{Session:has('failAuth')}}";
if (failAuth)
{
alert("Authentication failed. Please resubmit your form"); //or whatever code you wanna execute
}
</script>
Hope it helps =)
You can use following code to achieve this:
You can refer to this at Laravel Validation
if ($validator->fails()) {
return redirect('your_form_url')
->withErrors($validator)
->withInput($input_variable);//optional
}
You can then display these errors by referencing $errors variable in your view.
Note: The $errors variable is bound to the view by the Illuminate\View\Middleware\ShareErrorsFromSession middleware, which is provided by the web middleware group. When this middleware is applied an $errors variable will always be available in your views, allowing you to conveniently assume the $errors variable is always defined and can be safely used.

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

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.

Resources