I am trying to send a POST request using Guzzle to a route defined in my routes/web.php from a model. Both the model and the controller are defined in the same Laravel application. The controller action linked to the route returns a JSON response and works fine when called from javascript using Ajax. However, when I try to do this using Guzzle, I have the following error:
GuzzleHttp \ Exception \ ClientException (419)
Client error: `POST https://dev.application.com/login` resulted in a `419 unknown status` response
When searching for a solution, I read that it may be caused by a missing csrf token, so I added it to my reuqest, but I still get the same error.
Here's the model code that uses Guzzle to send the request:
$client = new Client();
$response = $client->post(APPLICATION_URL.'login', [
'headers' => [
'X-CSRF-Token' => csrf_token()
],
'form_params' => [
'socialNetwork' => 'L',
'id_token' => $id
],
]);
APPLICATION_URL is simply the base URL of the application, starting with https://.
Am I missing something? Thanks in advance!
Don't send requests internally in your app, forward the call by dispatching post requests to routes instead
This method seems faster than using an HTTP client library like Guzzle
Your code should look something like this
$request = Request::create(APPLICATION_URL . 'login', 'POST', [
'socialNetwork' => 'L',
'id_token' => $id
]);
$request->headers->set('X-CSRF-TOKEN', csrf_token());
$response = app()->handle($request);
$response = json_decode($response->getContent(), true);
Update
You have to manually handle the response from internally dispatched routes, here's an example to get started
web.php
use Illuminate\Http\Request;
Route::get('/', function () {
$request = Request::create('/test', 'POST', ['var' => 'bar']);
$request->headers->set('X-CSRF-TOKEN', csrf_token());
$response = app()->handle($request);
$responseContent = json_decode($response->getContent(), true);
return $responseContent;
});
Route::post('test', function () {
$upperCaseVar = strtoupper(request()->var);
return response(['foo' => $upperCaseVar]);
});
Access / route by GET request and get response from /test as if it's POST request
Result
{
"foo": "BAR"
}
Hope this helps
Related
Currently, I'm using Laravel HTTP client to make a request to an external URL. Mostly, the package working fine until I try to implement on_stats option from Guzzle.
From the doc, it says we can use Guzzle options using withMethod() method.
Here is my sample code to implement on_stats option using HTTP client.
$response = Http::withOptions([
'debug' => true,
'on_stats' => function(\GuzzleHttp\TransferStats $stats) {
Log::debug($stats->getTransferTime());
}
])
->get('https://laravel.com');
dd($response->status());
The code above will produce an error with the message:
Second array member is not a valid method
However, when I'm using the option within the Guzzle package directly, it works fine.
$client = new \GuzzleHttp\Client;
$response = $client->get('https://laravel.com', [
'on_stats' => function(\GuzzleHttp\TransferStats $stats) {
Log::debug($stats->getTransferTime());
}
]);
dd((string) $response->getStatusCode());
Any idea why this is happening? Is it a bug from the HTTP client wrapper from Laravel?
FYI, I'm using Laravel 8.x.
Thanks.
withOptions uses this code:
return tap($this, function ($request) use ($options) {
return $this->options = array_merge_recursive($this->options, $options);
});
So I'm guessing passing a closure in may not work, since it's not actually an array. From https://laracasts.com/discuss/channels/requests/httpwithtoken-get-total-time-of-request , you can get it from the response instead, so try this:
$client = new \GuzzleHttp\Client;
$response = $client->get('https://laravel.com');
Log::debug($response->transferStats->getTransferTime());
I'm trying to verify the idToken provided from firebase javascript sdk with the Tuupola Jwt middleware for slim 4 but I always get a 401 error. This is the client code I'm using to get the token:
const provider = new firebase.auth.GoogleAuthProvider();
provider.addScope("profile");
provider.addScope("email");
firebase.auth().signInWithPopup(provider).then( (result) => {
console.log(result);
});
The auth flow will work correctly as expected and I'm able to pass the token into the Authorization header but I'm not able to verify it on the server where I'm using slim 4 for a Restful api.
I've read different question about this problem but none of this have helped me to solve this problem.
here is my middleware implementation:
use Tuupola\Middleware\CorsMiddleware;
use Tuupola\Middleware\JwtAuthentication;
use Slim\App as App;
return function(App $app) {
$app->add(new Tuupola\Middleware\CorsMiddleware([
"origin" => ["chrome-extension://oegddbimpfdpbojkmfibkebnagidflfc"],
"methods" => ["GET", "POST", "OPTIONS"],
"headers.allow" => ["Authorization"],
"headers.expose" => [],
"credentials" => true,
"cache" => 86400
]));
// $rawPublicKeys = file_get_contents('https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com');
// $keys = json_decode($rawPublicKeys, true);
$keys = file_get_contents('https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com');
$app->add(new Tuupola\Middleware\JwtAuthentication([
"algorithm" => ["RS256"],
"header" => "X-Authorization",
"regexp" => "/Bearer\s+(.*)$/i",
"secret" => $keys,
"secure" => false,
"after" => function ($response, $arguments) {
return $response->withHeader("X-Brawndo", "plants crave"); //this is only for test
}
]));
};
and this is what I have inside my index.php file where slim app is running
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Routing\RouteCollectorProxy;
use Slim\Routing\RouteContext;
use Slim\Factory\AppFactory;
use Tuupola\Middleware\CorsMiddleware;
require_once __DIR__.'/vendor/autoload.php';
$app = AppFactory::create();
$authMiddleware = require_once __DIR__.'/middleware.php';
$authMiddleware($app);
$app->get('/keygen', function(Request $request, Response $response, $args){
$password = bin2hex(random_bytes(3));
$response->getBody()->write( json_encode(['generated_password' => $password]) );
return $response->withHeader('Content-Type','application/json');
});
$app->add(new Tuupola\Middleware\CorsMiddleware([
"origin" => ["*"],
"methods" => ["GET", "POST", "OPTIONS"],
"headers.allow" => ["Authorization"],
"headers.expose" => [],
"credentials" => true,
"cache" => 86400
]));
$app->run();
What I want to achive is to authenticate each request made from the client to the api using the firebase idToken provided after client login. When a request is made, the middleware will verify the token and then authorize the user or not to use the endpoint.
Is possible to fix this?
After a lot of debug I've found and solved the problem. In my client code I was using the wrong idToken as Authorization: Bearer and also the header sended to the server was mismatching the middelware configuration, in my axios requests I was sending the X-Authorization header instead of Authorization. To get the correct token to use I've called firebase.auth().onAuthStateChanged( (user) =>{...}) method and when the user object become available I've called the getIdToken() method. This operation return the correct JWT token to use with the middleware to authenticate the requests.
This is related to Laravel 5.4 and its Passport Password Grant.
I have routes for obtaining an access_token for the user to use that works completely fine.
I also have a route to refresh the token should the current access token expire.
When I use Postman to hit the route
http://192.168.10.10/refresh
I get this back which is valid:
"data": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOi...deleting most of it.",
"expires_in": 600
}
However when I hit the route from the browser, using axios via this code:
let headers = { 'Content-type': 'application/json' }
return axios.post("http://192.168.10.10/refresh", {headers: headers}).then(res => {
if (res) return res;
}).catch(err => {
if (err) return err.response;
});
I get an HTTP 500 Error status code.
I'm tailing the laravel log for errors as well and this is the stack trace.
[2017-08-30 07:21:41] local.ERROR: GuzzleHttp\Exception\ClientException: Client error: POST http://192.168.10.10/oauth/token resulted in a 400 Bad Request response:
{"error":"invalid_request","message":"The request is missing a required parameter, includes an invalid parameter value, (truncated...)
in /home/vagrant/Code/work/vendorgraphs-api/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php:113
The other part of this error is that it may be a malformed value.
Doesn't make sense to me and I've tried everything from making curl requests directly from PHP code and also used http_query_builder functionality.
Cache-Control →no-cache, private
Connection →keep-alive
Content-Type →application/json
This is what's set on Postman with the request. I am sending those headers from the browser as well. Any ideas on what might be causing the issue? This is driving me crazy.
public function refreshToken(Request $request)
{
if (!is_null($request)) {
$refreshToken = $request->cookie(self::REFRESH_TOKEN);
$http = new Guzzle();
$response = $http->request('POST','http://192.168.10.10/oauth/token', [
'form_params' => [
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken,
'client_id' => env("PASSWORD_GRANT_CLIENT_ID"),
'client_secret' => env("PASSWORD_GRANT_SECRET"),
'scope' => '',
]
]);
$data = json_decode($response->getBody());
$this->cookie->queue(
self::REFRESH_TOKEN,
$data->refresh_token,
864000, // 10 days
null,
null,
false,
true // HttpOnly
);
return response()->json([
'payload' => [
'access_token' => $data->access_token,
'expires_in' => $data->expires_in
]
]);
} else {
return response()->json("Could not refresh token", 401);
}
}
And here's the content for the postman request.
HERE'S WHERE I'M CONFUSED.
No parameters are being passed through this POST request.
CORS is enabled on my api, not receiving pre-flight errors.
Headers are the same on both requests.
The only difference is, Postman being used and Axios being used.
Nothing changes, except for where the request is being made, via Postman or browser.
content-type in your browser is text/html
whereas in the postman is application/json
Set the content-type to application/json and it should work
What I want is get an object from an API with a HTTP (eg, jQuery's AJAX) request to an external api. How do I start? I did research on Mr Google but I can't find anything helping.
Im starting to wonder is this is even possible?
In this post Laravel 4 make post request from controller to external url with data it looks like it can be done. But there's no example nor any source where to find some documentation.
Please help me out?
Based upon an answer of a similar question here:
https://stackoverflow.com/a/22695523/1412268
Take a look at Guzzle
$client = new GuzzleHttp\Client();
$res = $client->get('https://api.github.com/user', ['auth' => ['user', 'pass']]);
echo $res->getStatusCode(); // 200
echo $res->getBody(); // { "type": "User", ....
We can use package Guzzle in Laravel, it is a PHP HTTP client to send HTTP requests.
You can install Guzzle through composer
composer require guzzlehttp/guzzle:~6.0
Or you can specify Guzzle as a dependency in your project's existing composer.json
{
"require": {
"guzzlehttp/guzzle": "~6.0"
}
}
Example code in laravel 5 using Guzzle as shown below,
use GuzzleHttp\Client;
class yourController extends Controller {
public function saveApiData()
{
$client = new Client();
$res = $client->request('POST', 'https://url_to_the_api', [
'form_params' => [
'client_id' => 'test_id',
'secret' => 'test_secret',
]
]);
echo $res->getStatusCode();
// 200
echo $res->getHeader('content-type');
// 'application/json; charset=utf8'
echo $res->getBody();
// {"type":"User"...'
}
You just want to call an external URL and use the results? PHP does this out of the box, if we're talking about a simple GET request to something serving JSON:
$json = json_decode(file_get_contents('http://host.com/api/stuff/1'), true);
If you want to do a post request, it's a little harder but there's loads of examples how to do this with curl.
So I guess the question is; what exactly do you want?
As of Laravel v7.X, the framework now comes with a minimal API wrapped around the Guzzle HTTP client. It provides an easy way to make get, post, put, patch, and delete requests using the HTTP Client:
use Illuminate\Support\Facades\Http;
$response = Http::get('http://test.com');
$response = Http::post('http://test.com');
$response = Http::put('http://test.com');
$response = Http::patch('http://test.com');
$response = Http::delete('http://test.com');
You can manage responses using the set of methods provided by the Illuminate\Http\Client\Response instance returned.
$response->body() : string;
$response->json() : array;
$response->status() : int;
$response->ok() : bool;
$response->successful() : bool;
$response->serverError() : bool;
$response->clientError() : bool;
$response->header($header) : string;
$response->headers() : array;
Please note that you will, of course, need to install Guzzle like so:
composer require guzzlehttp/guzzle
There are a lot more helpful features built-in and you can find out more about these set of the feature here: https://laravel.com/docs/7.x/http-client
This is definitely now the easiest way to make external API calls within Laravel.
Updated on March 21 2019
Add GuzzleHttp package using composer require guzzlehttp/guzzle:~6.3.3
Or you can specify Guzzle as a dependency in your project's composer.json
{
"require": {
"guzzlehttp/guzzle": "~6.3.3"
}
}
Include below line in the top of the class where you are calling the API
use GuzzleHttp\Client;
Add below code for making the request
$client = new Client();
$res = $client->request('POST', 'http://www.exmple.com/mydetails', [
'form_params' => [
'name' => 'george',
]
]);
if ($res->getStatusCode() == 200) { // 200 OK
$response_data = $res->getBody()->getContents();
}
Definitively, for any PHP project, you may want to use GuzzleHTTP for sending requests.
Guzzle has very nice documentation you can check here.
I just want to say that, you probably want to centralize the usage of the Client class of Guzzle in any component of your Laravel project (for example a trait) instead of being creating Client instances on several controllers and components of Laravel (as many articles and replies suggest).
I created a trait you can try to use, which allows you to send requests from any component of your Laravel project, just using it and calling to makeRequest.
namespace App\Traits;
use GuzzleHttp\Client;
trait ConsumesExternalServices
{
/**
* Send a request to any service
* #return string
*/
public function makeRequest($method, $requestUrl, $queryParams = [], $formParams = [], $headers = [], $hasFile = false)
{
$client = new Client([
'base_uri' => $this->baseUri,
]);
$bodyType = 'form_params';
if ($hasFile) {
$bodyType = 'multipart';
$multipart = [];
foreach ($formParams as $name => $contents) {
$multipart[] = [
'name' => $name,
'contents' => $contents
];
}
}
$response = $client->request($method, $requestUrl, [
'query' => $queryParams,
$bodyType => $hasFile ? $multipart : $formParams,
'headers' => $headers,
]);
$response = $response->getBody()->getContents();
return $response;
}
}
Notice this trait can even handle files sending.
If you want more details about this trait and some other stuff to integrate this trait to Laravel, check this article. Additionally, if interested in this topic or need major assistance, you can take my course which guides you in the whole process.
I hope it helps all of you.
Best wishes :)
Basic Solution for Laravel 8 is
use Illuminate\Support\Facades\Http;
$response = Http::get('http://example.com');
I had conflict between "GuzzleHTTP sending requests" and "Illuminate\Http\Request;" don't ask me why... [it's here to be searchable]
So looking for 1sec i found in Laravel 8 Doc...
**Guzzle is inside the Laravel 8 Http Request !**
https://laravel.com/docs/8.x/http-client#making-requests
as you can see
https://laravel.com/docs/8.x/http-client#introduction
Laravel provides an expressive, minimal API around the Guzzle HTTP
client, allowing you to quickly make outgoing HTTP requests to
communicate with other web applications. Laravel's wrapper around
Guzzle is focused on its most common use cases and a wonderful
developer experience.
It worked for me very well, have fun and if helpful point up!
I also created trait similar to #JuanDMeGonthat's that u can use anywhere in your project.Please check this out
trait ApiRequests
{
public function get($url, $data = null)
{
try {
$response = Http::get($this->base_url . $url, $data);
} catch (\Exception $e) {
info($e->getMessage());
abort(503);
}
if ( $response->status() == 401) {
throw new AuthenticationException();
} else if (! $response->successful()) {
abort(503);
}
return $response->json();
}
public function post($url, $data = [])
{
$token = session()->get('token');
try {
$response = Http::acceptJson()->withToken($token)->post($this->base_url . $url, $data);
} catch (\Exception $e) {
abort(503);
}
if ($response->status() == 401 && !request()->routeIs('login')) {
throw new AuthenticationException();
}
return $response;
}
}
class Controller extends BaseController
{
protected $base_url;
use AuthorizesRequests, DispatchesJobs, ValidatesRequests, ApiRequests;
public function __construct()
{
$this->base_url = env("BASE_URL","http://192.168.xxxxxxx");
View::share('base_url', $this->base_url);
}
}
You can use Httpful :
Website : http://phphttpclient.com/
Github : https://github.com/nategood/httpful
Here is the simple call for laravel 9.4
Route::get('/currency', function () {
$response = Http::withHeaders([
'x-api-key' => 'prtl6749387986743898559646983194',
])->get('https://partners.api.skyscanner.net/apiservices/v3/culture/currencies');
return response()->json(['status'=> true,'data'=> json_decode($response->body()), 'Message'=>"Currency retrieved successfully"], 200);
});
Don't forget to import
use Illuminate\Support\Facades\Http;
I'm new to laravel, and I'm trying to implement a simple rest api.
I have the controller implemented, and tested via unit testing.
My problem is with the POST request.
Via the tests Input:json has data, via an external rest client it returns null.
This is the code on the unit test
$newMenu = array(
'name'=>'Christmas Menu',
'description'=>'Christmas Menu',
'img_url'=>'http://www.example.com',
'type_id'=>1,
);
Request::setMethod('POST');
Input::$json = $newMenu;
$response = Controller::call('menu#index');
What am I doing wrong?
UPDATE:
This is realy driving me crazy
I've instanciated a new laravel project and just have this code:
Routes
Route::get('test', 'home#index');
Route::post('test', 'home#index');
Controller:
class Home_Controller extends Base_Controller {
public $restful = true;
public function get_index()
{
return Response::json(['test'=>'hello world']);
}
public function post_index()
{
return Response::json(['test'=>Input::all()]);
}
}
CURL call:
curl -H "Accept:application/json" -H"Content-type: application/json" -X POST -d '{"title":"world"}' http://localhost/laravel-post/public/test
response:
{"test":[]}
Can anyone point me to what is wrong.
This is really preventing me to use laravel, and I really liked the concept.
Because you are posting JSON as your HTTP body you don't get it with Input::all();
You should use:
$postInput = file_get_contents('php://input');
$data = json_decode($postInput, true);
$response = array('test' => $data);
return Response::json($response);
Also you can use
Route::any('test', 'home#index');
instead of
Route::get('test', 'home#index');
Route::post('test', 'home#index');
Remove header Content-type: application/json if you are sending it as key value pairs and not a json
If you use : Route::post('test', 'XYZController#test');
Send data format : Content-type : application/json
For example : {"data":"foo bar"}
And you can get the post (any others:get, put...etc) data with :
Input::get('data');
This is clearly written in here : http://laravel.com/docs/requests
. Correct Content-type is very important!
I am not sure your CURL call is correct. Maybe this can be helpful : How to POST JSON data with Curl from Terminal/Commandline to Test Spring REST?
I am using Input::get('data') and it works.
I was facing this problem, my response of post was always null. To solve that I put the body key in guzzle object, like this
$client = new Client([
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => config('app.callisto_token'),
]
]);
$body = [
'firstResult'=> 0,
'data' => '05/05/2022'
];
$response = $client->post('http://'.$this->ip.'/IntegracaoERP'.'/status_pedido',
['body' => json_encode($body)]
);
Don't forget the json_encode in body key.
Hope this helps.