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
Related
I am new to Laravel and I am trying to make a function to create a database row but my routes are not working.
I currenlty have this in my web.php file:
Route::get('/admin/pagina', [PaginaOverzichtController::class, 'index'])
->name('Admin_Pagina_Overzicht')
->middleware('auth');
Route::post('/admin/pagina', [PaginaOverzichtController::class, 'CreatePage'])
->name('Admin_Pagina_CreatePage')
->middleware('auth');
Route::post('/admin/pagina', [PaginaOverzichtController::class, 'DeletePage'])
->name('Admin_Pagina_DeletePage')
->middleware('auth');
But when I go to /admin/pagina, I get a Route [Admin_Pagina_CreatePage] not defined error.
Am I allowed to have the same URL but different name pointing to a different function in the same controller? If not, is there a best-practice way to do this?
^
I have a form on my page that should create a page with the method post and action {{ route('Admin_Pagina_CreatePage') }}
Am I allowed to have the same url but differend name pointing to a differend function in the same controller?
No. When determining uniqueness in Laravel routes, the HTTP Method & URI act as a combined primary key. Everything else is just metadata attached to that unique entry.
In your example, the second Route::post('/admin/pagina') is overwriting the first one, because you've defined the same "ID" pair of POST /admin/pagina.
I'm not sure how you expect to have the same HTTP method and URI go to two separate controller actions. If you expect to route them differently based on what's included in the request body, that conflicts with how Laravel's routing works (routes are found and dispatched without usage of the request body).
Normally for a delete, you would utilize that HTTP method in the routing:
Route::delete('/admin/pagina' [/* ... */]);
This can be paired with form method spoofing to trick a normal form request (which doesn't support DELETE) to find that appropriate route anyway.
Not related to your problem it's just a suggestion, you can use Route Groups and assign a middleware to that group to avoid repetition of assigning one middleware to each route.
E.g.
Route::middleware(['auth'])->group(function(){
Route::get('/admin/pagina', [PaginaOverzichtController::class, 'index'])->name('Admin_Pagina_Overzicht');
Route::post('/admin/pagina', [PaginaOverzichtController::class, 'CreatePage'])->name('Admin_Pagina_CreatePage');
Route::post('/admin/pagina', [PaginaOverzichtController::class, 'DeletePage'])->name('Admin_Pagina_DeletePage');
});
I am just learning laravel resource methods to build a basic API. Below is the code of my api.php file that shows all the API routes.
// List Articles
Route::get('articles', 'ArticleController#index');
// List Single Article
Route::get('article/{id}', 'ArticleController#show');
// Create New Article
Route::post('article', 'ArticleController#store');
// Update Article
Route::put('article', 'ArticleController#store');
// Delete Article
Route::delete('article/{id}', 'ArticleController#destroy');
This works perfectly on get and delete methods. But for Post method, it is throwing error "405 Method not allowed". I am using Postman to test the API calls.
To be specific, below is the exact error Postman shows
Symfony \ Component \ HttpKernel \ Exception \ MethodNotAllowedHttpException
Also attaching screenshot of Postman
Change you store route like this:
Route::post('article/store', 'ArticleController#store');
Because you send post request from Postman to
/article/store
A MethodNotAllowedHttpException indicates the POST route can not be found for the requested url, but other methods are available.
This can be because you did not define it (correctly), or it has a conflict with another route in your config.
You can check the current routes with php artisan route:list
If you want to use resource controllers, instead of defining all the resource routes and actions yourself, why are you not using the Route::resource() method?
Route::resource('article', ArticleController::class);
This will generate all resource routes for you:
Verb Path Action Route Name
GET /article index article.index
GET /article/create create article.create
POST /article store article.store
GET /article/{article} show article.show
GET /article/{article}/edit edit article.edit
PUT/PATCH /article/{article} update article.update
DELETE /article/{article} destroy article.destroy
The action translates to the action name in your controller, so for example, a request to POST /article will call the controller action: ArticleController#store.
In your case, I see that you are not using create or edit views, so instead of using the Route::resource() method, you can use the Route::apiResource() method, which will exclude routes that present HTML views for creating and editing your articles.
Route::apiResource('article', Api\ArticleController::class);
This will create your routes like:
Verb Path Action Route Name
GET /article index article.index
POST /article store article.store
GET /article/{article} show article.show
PUT/PATCH /article/{article} update article.update
DELETE /article/{article} destroy article.destroy
You can also auto-generate the resource controller to match your resource routes, this will generate the controller file for you.
php artisan make:controller Api/ArticleController --api
This will generate that file in Http/Controllers/Api/ArticleController with a mock of all the actions defined by the route which you can then use.
More info on resource controllers
PS.
Your PUT route does not take an id and it calls store, it is good practice to split the actions for POST (creating new) and PUT/PATCH (full/partial update of existing objects) in your controller.
Reason for this is that by convention, POST will create a new entity and doing a post again will (most likely) create another, so every request will have a different result.
PUT requests, on the other hand, are idempotent, meaning you should be able to do a PUT request multiple times on the same object and the output should be the same for all these requests. PATCH is a bit of a weird one here, it can be idempotent, but is not required. But when using Laravel, PATCH requests are usually handled by the same controller action which handles the PUT requests, and (depending on implementation) will be idempotent.
PSS.
I would not recommend using POST /article/store and follow the REST convention of doing a POST on the resource name itself instead. POST /article
If sll is active on your domain, you should request like HTTPS://yourdomain.com . You should check that, then try again.
The methodNotAllowed exception indicates that a route doesn't exist for the HTTP method you are requesting.
Your form is set up to make a POST request, so your route needs to use Route::post() to receive this.
Be sure that request on postman is set on POST
Remember to clear route cache after any route change:
php artisan route:cache
Try also changing settings on postman: Do not send anything in headers - I mean delete Content-Type
I'm currently re-writing an API with multiple endpoints. However, for legacy purposes it's required that there is a single endpoint which can be used to access all other endpoints. Which endpoint we should redirect to is based upon a custom action header send along with the request.
Example:
Input: Header -> Action A
Output: Redirect to route '/some/url' 'ControllerA#someAction'
Input: Header -> Action B
Output: Redirect to route '/some/other/url' 'ControllerB#someOtherAction'
Normally, I could use the redirect() method but then I lose the body of the POST method. All the endpoints I declared are POST methods.
Basically, the question is how can I properly redirect a POST to another route?
Also note I can't use:
App::call('App\Http\Controllers\PlanningController#addOrUpdate', ['request' => $request]);
Since my method uses a custom Request class to handle the validation. I get an exception telling the argument should be the type of my custom class and Illuminate\Http\Request is given.
I've actually found the answer to my problem. I've created a middleware which will re-create the request based upon the value found in the header.
Here's the handle function of the middleware (only tested on Laravel 5.2):
use Request;
use Route;
use Illuminate\Http\Response;
...
public function handle($request, Closure $next, $guard = null)
{
// Get the header value
$action = $request->header('action');
// Find the route by the action name
$route = Actions::getRouteByName(action); // This returns some route, i.e.: 'api/v1/some/url'
// Perform the action
$request = Request::create(route, 'POST', ['body' => $request->getContent()]);
$response = Route::dispatch($request);
return new Response($response->getContent(), $response->status(), ['Content-Type' => 'text/xml']); // the last param can be any headers you like
}
Please note that this might conflict on your project with other middleware. I've disabled other middleware and created a special routegroup for this. Since we're redirecting the call manually to another route the middleware on that route is called anyway. However, you can also implement this code inside a controller function then there are no conflicting middleware problems!
I'm building an API for user and admin.
Got stuck at edit user profile routing.
on admin route i use Route::resource('user', 'UserController')
on user route i use Route::get('profile', 'UserController#show')
At the show method Laravel default has
public function show($id)
{
}
the different between them is on admin I can use /id but on user i check their token from middleware and merge the request to get their user_id so there is no need for the API to use profile/{id}.
The question is how can I use the same method but there is an argument to fill and the route still /profile?
One of my solution is :
public function show($id){
if ($request->has('user_id')):
$id = $request->query('user_id');
endif;
}
It working but when i read the code, it's really redundant always checking it and replace the id.
Just place the request object as a parameter in your controller and get the input from the request object when you use your user route.
Thanks
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