Laravel. Redirect intended to post method - php

I'm using laravel 5 and this is my problem. User fill in form X and if he isin't logged in, he gets redirected to fill in more fields form OR he gets possibility to log in. Everything works just fine, if user fill in additional fields, but if he login, laravel redirects user to form X with GET method instead of POST.
This is how my middleware redirect looks like:
return redirect()->guest('user/additional-fields');
This redirect appears on successfull log in:
return redirect()->intended();
So on redirect intended i get error
MethodNotAllowedHttpException. URL is correct which is defined as POST method. What am I missing here? Why does laravel redirects intended as GET method? How could I solve this problem? Thanks!
EDIT:
Route::post('/user/log-in-post', ['as' => 'user-log-in-post', 'uses' => 'UserController#postUserLogIn']);
This is my route, I hope this is one you need.

You can use a named route to solve this issue:
Lets make a named route like this:
For Get
Route::get('user/additional-fields',array(
'uses' => 'UserController#getAdditionalFields',
'as' => 'user.getAdditionalFields'
));
For post
Route::post('user/additional-fields',array(
'uses' => 'UserController#postAdditionalFields',
'as' => 'user.postAdditionalFields'
));
So we can now ensure Laravel uses the right route by doing this
return redirect()->guest(route('user.getAdditionalFields'));
Also note that its not possible to redirect a POST because Laravel expects form to be submitted. SO you can't do this:
return redirect()->guest(route('user.postAdditionalFields'));
except you use something like cURL or GuzzleHttp simulate a post request

You have to trick Laravel router by passing an "_method" the inputs.
The best way I found is by adding tricking and rewriting the Authenticate middleware
You have to rewrite the handle method to allow your redirection with your new input.
redirect()->guest('your/path')->with('_method', session('url.entended.method', 'GET'));
When you want to redirect to a route using another method than GET, simply do a Session::flash('url.entended.method', 'YOUR_METHOD').
Tell me if it do the trick

Very Simple Approach for Post method Route form Controller.
The idea behind this is, every Route always calls the Action method of a Controller. so that in that case you can directly call that method in place of Redirect action performed.
check a code sample of XYZController
$registration = Registration::find($req->regId);
$registration->update([ 'STEP_COMPLETED' => 5]); // Step 5 completed.
# Call Post Method Route
return $this->admissionFinish($req);
Note that $req should have all parameter that required in next
action Method.

change the below code in app\exceptions\handler.php
use Exception;
use Request;
use Illuminate\Auth\AuthenticationException;
use Response;
protected function unauthenticated($request,AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Unauthenticated.'], 401);
}
//return redirect()->guest(route('login'));
return redirect()->guest('http://127.0.0.1:8000/api/signinnew'); // change this part to your login router
}
And in routes(i.e api.php):
Route::Any('signinnew', [UserLogonController::class, 'signinNew']);
This will work in laravel 8x

Related

Declare same route twice but expect different behaviour according to a middleware

I started creating a REST API using the lumen framework and wanted to set up a particular behaviour for my GET /user route. Behaviour is the following:
If the request come from an authenticated user (using auth middleware), the method getAllFields from UserController is called and return all the data from the user
If it's not the case, the method get from UserController is called and return some of the data from the user
It seems logic to me to just write it like that in my web.php using a simple middleware:
<?php
$router->group(['middleware' => 'auth'], function () use ($router) {
$router->get('/user/{id}', [
'uses' => 'UserController#getAllFields'
]);
});
$router->get('/user/{id}', [
'uses' => 'UserController#get'
]);
But for some reason, even if the middleware is correct, I always get the response of the second route declaration (that call get()). I precise that if I remove the second route declaration, the one in the middleware work as expected.
Have someone an idea how I can achieve something similar that work?
Router will check if your request matches to any declared route. Middleware will run AFTER that match, so You cannot just return to router and try to find another match.
To fallow Laravel and Routes pattern - You should have single route that will point to method inside controller. Then inside that You can check if user is logged or not and execute getAllFields() from that controller. It will be not much to rewrite since You are currently using UserController in both routes anyway.
web.php
$router->get('/user/{id}', 'UserController#get');
UserController.php
public function get()
{
return auth()->check() ? YourMethodForLogged() : YourMethodForNotLogged();
}
Or if there is not much logic You can keep this in single method.
Also it is good idea to fallow Laravels REST standards (so use show instead of get, "users" instead of "user" etc - read more https://laravel.com/docs/7.x/controllers)
web.php
$router->get('/users/{user}', 'UserController#show');
UserController.php
public function show(User $user)
{
if (auth()->check()) {
//
} else {
//
}
}
To summary - for your needs use Auth inside controller instead of middleware.
To check if user is logged You can use Facade Auth::check() or helper auth()->check(), or opposite Auth::guest() or auth()->guest().
If you are actually using Lumen instead of full Laravel then there is not auth helper by default (You can make own or use package like lumen-helpers) or just keep it simple and use just Facades instead (if You have then enabled in Lumen).
Read more https://laravel.com/docs/7.x/authentication and https://lumen.laravel.com/docs/7.x/authentication
This pattern is against the idea of Laravel's routing. Each route should be defined once.
You can define your route without auth middleware enabled and then define your logic in the controller.

Middleware 'auth' clarification

Im using Laravel 5.8 and I got the following situation:
I have a simple form with a button which sends a delete request to a route. The form works like this: When the button is pressed, the form's action redirects me to the URL localhost/delete/4 where 4 is the id of the entry in the database, and there the route kicks in and the controller deletes my entry.
However, unauthenticated users do not have access to the form's button, and the route is protected by the middleware 'auth'.
But, if I, as an unauthenticated user, type in the adress bar localhost/delete/4, I get a method unsupported error, which is expected because I send a get request to a delete type route.
But my question is why do I get this error at all? Since the route is protected by the middleware against unauthenticated users, why does the request reach the route since it should be blocked by the middleware?
Below you got the route:
Route::delete('/delete/{id}', ['uses' => 'LibraryController#getLibraryDelete', 'middleware' => 'auth']);
Oh, as a side note, if a change the route to receive get requests, and try again, the middleware works fine
The route is checked first before going to the middleware and controller...
So if the route was not found actually the script doesn't know which middleware or controller to go to...
--
Here is a good use case for example someone want to define the following routes
Route::get('/question/{id}', 'QuestionController#view');
GET /question/1 is public for all users and returns the question itself (read only)
but
Route::patch('/question/{id}', 'QuestionController#edit')->middleware('auth');
PATCH /question/1 is only authenticated user can edit question...
So it's acceptable that different methods can have different middlewares or no middlewares for the same route...
And that some methods are not defined/allowed
--
The method is unsupported because your defined route is for deletes only as in ::delete method you used
Delete request is either a HTTP POST request with a query called "_method" and value "delete" or in supported browser an HTTP DELETE request
When the user type the url manually in their address bar it's a GET request which can be handled by this route method ::get
Available routing methods from latest documentation: (https://laravel.com/docs/5.8/routing)
Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);
Plus special route ::any which accepts any method
In laravel if the user reach a url that is defined but with a Method that's not defined in routes you get this "Method unsupported"
The method unsupported error is irrelevant to auth middleware in this case ... it's just about routing

How do I redirect back when validation fails in Laravel 5.4?

I'm working on a Laravel 5.4 project and have multiple pages with the same url e.g. www.blahblah.com/order/verify/{encryption_key}
My routs are:
Route::get('/order/verify/{encrypted_key}','PinVerificationController#init');
Route::post('/order/verify/{encrypted_key}','PinVerificationController#pinValidation');
The flow is they land on a page first where they enter their phone number, then they go to a second page where they have to enter a pin code. I validate if the pin code is a number, if it isn't then I redirect back with an error message. But they're being redirected to the first page instead.
If the validation fails i'm routing back. I'm doing
return \Redirect::back()->withInput()->withErrors($validator);
but this is routing to the GET page instead of the POST page.
Why is this happening?
UPDATE #1
public function init(){
$country_extensions = appUtils::getCountryExtensionDropdown();
//TODO
$country_iso_code = "1-US";
$parameters = compact( 'country_extensions','country_iso_code' );
return view('/pages/choose_phone_verify_method',$parameters);
}
private function pinValidation(Request $request){
$validator = \Validator::make($request->all(), [
'pin_number' => 'required|numeric'
]);
if ($validator->fails()) {
return \Redirect::back()->withInput()->withErrors($validator);
}
}
I don't know if you make your validation in a controller or in a request. But as I can see you redirect back(), and it must be from your controller.
My suggestion is you use the formRequest class instead of the validator in your controller.
You see, the getRedirectUrl() method of the FormRequest class, tests for some special properties on the class, and if it doesn't find any value, it falls back to a redirect, using the Illuminate\Routing\UrlGenerator::previous() generated URL. Those properties that the FormRequest checks, are the redirection options you have.
Now you have two options of changing them, either globally in every form request you make, by putting the property in the abstract class App\Http\Requests\Request that every form request class inherits from. Or, in particular, form classes, by simply putting them in the form class itself.
And these are all the options you have for custom redirections :
protected $redirect; // A simple URL. ex: google.com
protected $redirectRoute; // A route name to redirect to.
protected $redirectAction; // A controller action to redirect to.
But if you insist do the validation in your controller you can write an if statement. so that if the validator fails it redirect to a specific path like page 2 path in this situation. like this code below:
if ($validator->fails()) {
return redirect('path to page 2')->withInput()->withErrors($validator);
}
Or you can redirect to route name:
if ($validator->fails()) {
return redirect(route('route name'))->withInput()->withErrors($validator);
}
Wouldn't it be easier to just handle the post request in the same method (init()).
That way you would need to redirect, but just display the errors.
And the user could easily correct his errors (since the form could be filled out, and it's automatically shown again) and submit the form again.

Laravel redirect after update uses PUT request

I have Laravel app with Vue on front end, and Vue calls update method from controller using PUT request.
Request works, model gets updated, but I have issue with redirecting as it is redirecting also as a PUT instead of simple GET?
public function update(MomentsValidationRequest $request, Project $project, Task $task, Moment $moment)
{
foreach($request->materials as $material){
$material_id_array[$material['id']] = ['quantity' => $material['quantity']];
}
$moment->update($request->all());
if(isset($material_id_array))
$moment->materials()->sync($material_id_array);
return redirect()->back()->with(['alert-type' => 'success', 'message' => 'Moment updated!']);
}
So naturally, I am getting a method not allowed exception because it is redirecting to a route which is supposed to get a previous view only.
Route itself is fine, request method isn't.
For non-believers :)
Also a route:
I know this is a bit late. But incase anyone stumbles across this.
You state that you're using Vue in the front end. This would suggest that the put request is being made through an axios call.
I can't see this call, so this is only an assumption. But I believe the solution would be to return a json object instead of a response in the controller, and then redirect trigger a redirect from the Vue component itself.
In the controller:
Session::flash('alert-type', 'success');
Session::flash('message', 'Moment updated!');
return response()->json(true);
In the component:
axios.post('/moments', this.moment).then(() => {
window.location.replace("moments");
});
I believe this is something to do with how axios handles patch requests, it seems to attempt to handle a redirect response automatically, I could be wrong though, so any reply is welcome to if there's a better explanation.
You can use:
redirect()->back(303)->with(...)
No, redirection is made always with GET but you don't have such route defined. So you should create GET route that will do something with this.
It's possible only to redirect to GET routes.

GET and POST controller for one route in Laravel

I want to have one route rule for both GET and POST methods that redirect to one controller. Problem is GET doesn't require any parameters (it returns a view) but POST will have some parameters that are sent through a form.
In ASP.NET MVC5 I do it with one route rule and two controller methods with the same name but one of them (the POST method) has a [HttpPost] attribute and the parameters it needs while the GET method doesn't have any parameter or attribute.
How does one implement something like that in Laravel 5.x?
This is the possible controller:
public function convertUrl($somedataforpost)
{
if(Request::isMethod('get'))
{
// return a view
}
if(Request::isMethod('post'))
{
// do something with POST data
}
}
This is an example of how you would implement the rule:
Route::match(['get', 'post'], 'order/{invoice}/confirm', ['uses' => 'OrderController#paymentConfirmation', 'as' => 'order.payment.confirmation']);

Categories