I have a laravel api that I'm trying to run a custom artisan command to process a transaction. The api is suppose to check for pending transactions in our merchant database and post them in our transaction database.
I get the following error:
[GuzzleHttp\Exception\ClientException]
Client error: `POST http://paycentral.mymarket.com/transactions/bulk` resulted in a `405 Method Not Allowed` response:
{"error":{"message":"405 Method Not Allowed","status_code":405,"debug":{"line":446,"file":"\/var\/www\/vhosts\/maindomai (truncated...)
The API I'm using is located at api.mymarket.com. Searching for errors like this has me believing it's a CORS-related issue. I'm using laravel-cors and added Header set Access-Control-Allow-Origin "*" to the .htaccess in both the public folder for api.mymarket.com and paycentral.mymarket.com. The error is still persisting though. Is there any other possible workaround? We are currently using plesk for our hosting services.
UPDATE: I tried doing a preflight request in the pay subdomain
Origin: api.mymarket.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: MM
It returned a 500 Internal Error which is progress I guess.
UPDATE Here is the routes.php for paycentral. The cors-library is registered in the app.php.
paycentral routes.php
<?php
$api = app('Dingo\Api\Routing\Router');
// all routes are protected by the Authenticate middleware which makes sure the client
// is authenticated as *somebody* - each resource is further protected by the authorization
// policies in the App\Api\V1\Policies files to limit the method calls by which client
// type is attempting to access the resource - these must be mapped in the AuthServiceProvider
$api->group([
'version' => 'v1',
'namespace' => 'App\Api\V1\Controllers',
'middleware' => 'auth' // use the Authenticate middleware
], function($api) {
/*
* partial CRUD resource routes
*/
$api->get('transactions/{id}', 'TransactionController#show');
$api->post('transactions', 'TransactionController#store');
$api->put('transactions/{id}', 'TransactionController#update');
$api->post('transactions/bulk', 'TransactionController#store_bulk');
$api->post('transactions/get_updates', 'TransactionController#get_updates');
I solved the issue. It was an issue with one of the routes not pointing to transactions/bulk. The previous developer made undocumented changes to a couple files without following our version control methods so the production branch was broken.
Assuming that your route is defined well in the routes.php, and that everything else is fine. Then you may try adding the following line in your filters.php
App::before(function ($request) {
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS');
}
Related
I've got a basic Laravel API that I'm trying to set up. I've set up a CORS middleware, and I'm making requests to the API from a different (local) domain (i.e., aaa.host is a React front-end making Axios requests to a bbb.host Laravel API back-end).
All GET requests successfully go through the API and return a response, but any POST requests I attempt fail. My understanding is that the CSRF token is only required in Laravel for web requests, not API requests. As such, I can't understand what I'm missing and why POST requests aren't going through. If I change a POST route to a GET route, it instantly works, so the fact that it's a POST route seems to be the issue.
Here's an example of what the failed POST request looks like in the Chrome console:
I've looked at the Laravel API docs, but I don't understand what I'm missing. Does anyone have any ideas? Also, for what it's worth, I'm using Laravel 5.8.29. Thank you.
Edit: Here's what routes/api.php looks like:
Route::middleware(['cors'])->group(function () {
Route::post('/missing-subpath-on-purpose/item/create', 'SomeController#createItem');
});
Again, if I change Route::post to Route::get and make an Axios GET request instead of a POST, it works fine.
Many thanks to nice_dev for identifying the real problem and helping me solve it. Turns out that the Laravel API was fine and sending POST requests to it from Postman worked fine.
The issue was with the browser itself. If you send a POST request with a Content-Type of application/json, the browser will force two requests to occur, which was causing all sorts of problems. (I don't know at the moment the reasons for this, but I'm sure browser-makers have their reasons for doing things this way.)
Nevertheless, by changing the default Content-Type for all Axios requests in the browser to application/x-www-form-urlencoded, it solved my problem. Thank you.
Edit: After playing around with this more, I realized that I did want to be making application/json requests, but doing so was causing the aforementioned issues with CORS.
To fix this, I created the following CORS middleware in Laravel:
<?php
namespace App\Http\Middleware;
use Closure;
class Cors {
public function handle($request, Closure $next) {
header('Access-Control-Allow-Headers: *');
header('Access-Control-Allow-Methods: *');
header('Access-Control-Allow-Origin: *');
return $next($request);
}
}
I then added it to the global middleware in Kernel.php as follows:
protected $middleware = [
..., // Other middleware here.
\App\Http\Middleware\Cors::class,
];
The mistake I had made is that the CORS middleware wasn't part of the global middleware, but the route middleware, which was causing the issues.
In my routes/api.php I have a POST method route as such:
Route::post('hello', 'MyController#greet');
In the app/Http/Controllers/MyController.php the greet() method is defined as such:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class MyController extends Controller
{
public function greet(Request $request)
{
return response()->json(['not found'], 404);
}
}
Now, depending on the URL protocol that was calling from, the response is returning totally different messages:
Calling from POST https://localhost/hello will return 404 Not Found response:
[
"not found"
]
as expected.
Wheres, when calling from POST http://localhost/hello (without https) it will return 405 Method Not Allowed response instead:
{
"message": "The GET method is not supported for this route. Supported methods: POST."
}
This is a problem because I'm setting up my server behind AWS ELB which accepts HTTPS traffic only but will route back to the Auto Scaling instances via HTTP within the local VPC network.
So my question is what is causing this and how can I prevent Laravel from returning a different response based on URL protocol?
EDIT: Note that this only happens when I specify the status code in the response()->json($data, [status code]). Otherwise, the response works normally as expected.
None of those has to work, because you have defined your route as a post. Your requests are 'get' requests. It seems there is middleware in a higher priority and it's being called when it's an HTTP request to control illegal requests.
Try returning different things like dd("Hello World"); if it's loading the page there has to be a bug or something.
Temporarily add a middleware to your route to redirect requests to one of HTTP or HTTPS protocols
I solved it.
It turns out that I was using the nginx configuration from https://laravel.com/docs/5.8/deployment#server-configuration and included the following line:
error_page 404 /index.php;
This causes all 404 responses to redirect to the /index.php page, which is not defined in my Laravel routes, and the path expects a GET instead of POST method. Hence, it returns the error message stated in my question above.
After removing the above config line in my nginx configuration, my POST requests with 404 responses are working correctly again.
Thanks for all the help anyway.
I'm trying to create REST API using Laravel. I'm using JWT (Tymon\JWTAuth) to authenticate users.
Here is part of my api.php file with /api routes:
Route::middleware('auth:api')->get("match/{id}", "ApiMatchController#getMatch");
Route::middleware('auth:api')->put("match/{id}", "ApiMatchController#editMatch");
Now, I'm sending GET request to /api/match/7. Authorized user gets match details as expected. Unauthorized user is redirected to root url / but I want user to stay on the url, I just want to return HTTP code 401 - Unauthorized. Where can I change this? I can do that inside of ApiMatchController#getMatch method but I would like middleware auth:api to do that for me. Is there any way how to do this?
Then, I'm sending PUT request to /api/match/7 with some data. Request from authorized user works just fine but unauthorized user now gets HTTP code 405 - Method Not Allowed (with debug info: Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException: The PUT method is not supported for this route. Supported methods: GET, HEAD.). Why? I cleared the route cache and as you can see, there IS a defined route in api.php. This behaviour really happens just with unauthorized user.
About the first part:
Authorized user gets match details as expected. Unauthorized user is redirected to root url / but I want user to stay on the url, I just want to return HTTP code 401 - Unauthorized. Where can I change this?
This is because your default guard is web, so in that case when a user tries to access a protected route it will be redirected to the home page (by default, this can also customized of course).
To change the default guard to api go to config/auth.php and change it like this:
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
Note: When making HTTP Requests, add this headers:
/** The following tells Laravel that you want a response in json format. */
Accept: application/json
/** The following is for POST/PUT/PATCH requests, it tells the request payload format. */
Content-type: application/json
About the second part:
Request from authorized user works just fine but unauthorized user now gets HTTP code 405 - Method Not Allowed
PHP doesn't handle well the PUT/PATCH/DELETE methods, in order to bypass this inconvinience do a POST request and then add a hidden _method field to the form.
The value sent with the _method field will be used as the HTTP request method:
Request body (the method is case sentitive):
Endpoint:
/api/match/7
Headers:
Accept: application/json
Content-type: application/json
Payload or Body:
_method: PUT
...
I have tried the combination of authorize method under controller, and Throwable mentioned in laravel docs:
https://laravel.com/docs/8.x/errors
try{
$this->authorize('create',Client::class);
} catch(Throwable $e)
{
echo $e->getMessage();
return false;
}
you can use same approach for your api responses.
I'm using Slim 3 Framework as my backend and a small self-written frontend (jQuery). In my frontend I have ajax commands to call my REST server.
Now I'm facing the problem that I can't use DELETE on my client because it is not matching the HTTP request method (GET).
405 Method not allowed. Must be one of: GET, PUT
The official documentation says said it is not allowed by default:
If your Slim Framework application has a route that matches the
current HTTP request URI but NOT the HTTP request method, the
application invokes its Not Allowed handler and returns a HTTP/1.1 405
Not Allowed response to the HTTP client.
Now I could use GET or PUT but that is not possibly because I already have those routes declared for other actions.
Slim Application Error:
The application could not run because of the following error:
Details
Type: FastRoute\BadRouteException
Message: Static route /api/v1/folders/ is shadowed by previously defined variable route /api/v1/folders/(.*) for method GET
// Folder routes
$this->group('/folders', function () {
$this->get('[/{params:.*}]', 'FolderController:index');
$this->post('', 'FolderController:create');
$this->put('[/{params:.*}]', 'FolderController:update');
$this->delete('/[/{params:.*}]', 'FolderController:delete');
})->add('AuthenticateMiddleware');
Could you please give me an advice on how to solve this? Isn't this a general problem in the REST-world so to speak, because I guess many frameworks act like Slim 3 and throw a 405 Method not allowed error in such particular situation where you want to use DELETE but can't because the click in the browser is GET?
As per my comment:
Is the failing request happening when you click on a link? <a></a> ? The request method has to be DELETE in order for Slim to invoke the right controller. Also note that your delete route has an extra [
Good luck !
I am building a REST user-microservice using Laravel 5.5 + Passport.
I am using the standard Passport::routes(), but I have had to modify the Auth::routes in order to make them return JSON responses, and to make them work with Passport.
I have added the following lines to my routes/web.php file:
Route::group(['middleware' => 'auth:api'], function () {
$this->post('logout', 'Auth\LoginController#logout')->name('logout');
});
This allows me to POST https://myapi/logout
If I make the call with the header "Authorization => Bearer TOKEN", I get a successful logout response.
If I provide no header at all, I get a "not authenticated" message (which is good)
However, if I provide the header with a revoked token, I get a recursive deadloop of the function: Illuminate\Auth\RequestGuard->user() (it keeps calling itself recursively until stack-overflow)
This is all done in the auth:api middleware, my logout code is not reached, but my LoginController constructor is called. Constructor code:
public function __construct(Application $app)
{
$this->apiConsumer = $app->make('apiconsumer');
$this->middleware('guest')
->except('logout');
}
I'm struggling to understand if it's my code causing this issue, or some combination of Laravel + passport + auth.
My first thought was that the auth:api middleware fails to authenticate the user, and as a result redirects the user to /home, where for some reason it's triggered again, recursively. But if that was the case, why would it work correctly with no header?
My current thinking is that the token in question does exist in the database, but Laravel is failing to figure out that it's revoked.
Any suggestions appreciated,
I found an answer (if not the answer) after a lot of research. It appears this is a Laravel bug (https://github.com/laravel/passport/issues/440). The solution is to add OAuthServerException to the $dontReport array in app/Exceptions/Handler.php:
class Handler extends ExceptionHandler
{
protected $dontReport = [
...
\League\OAuth2\Server\Exception\OAuthServerException::class,
];
}
This will avoid trying to log user information, thereby avoid the deadloop.
I have faced this in localhost. in my case, I have used xampp server and facing this issue
after creating a virtual host like "testlarave.test" then solve the error