What I am trying to do is to login to an external API and retrieve a JSON file. For this I am using Guzzle in Laravel.
I have setup a controller to do this:
$client = new Client([
'base_uri' => 'https://www.space-track.org',
'timeout' => 2.0,
]);
I access the JSON file using:
$response = $client->request('GET', '/basicspacedata/query/class/boxscore');
In order to get the JSON file I am required to login to the API. The API tutorial tells me:
Login by sending a HTTP POST request ('identity=your_username&password=your_password') to: https://www.space-track.org/ajaxauth/login
What I am unable to do is login to the API using Guzzle. I tried following a few Guzzle tutorials and using the 'auth' array to which none worked.
Basically, what I am unable to do is to login to the API using Guzzle.
Here is a basic flow that should work
// Initialize the client
$api = new Client([
'base_uri' => 'https://www.space-track.org',
'cookies' => true, // You have to have cookies turned on for this API to work
]);
// Login
$api->post('ajaxauth/login', [
'form_params' => [
'identity' => '<username>', // use your actual username
'password' => '<password>', // use your actual password
],
]);
// Fetch
$response = $api->get('basicspacedata/query/class/boxscore/format/json');
// and decode some data
$boxscore = json_decode($response->getBody()->getContents());
// And logout
$api->get('ajaxauth/logout');
dd($boxscore);
Now if it's not a one off request and you're planing on extensively using this API you can wrap this "ugliness" in your own service class that exposes a meaningful internal API that allows you to write then something along the lines of
$spaceTrack = new App\Services\SpaceTrack\Client();
$boxscore = $spaceTrack->getBoxscore();
dd($boxscore);
The following code always results in no Route found. But the Route does exist.
$client = static::createClient();
$crawler = $client->request(
'GET',
'/app_dev.php/admin/',
array(),
array(),
array("HTTP_HOST" => "dev.example:8080"));
But always fails. If I go to http://dev.example:8080/app_dev.php/admin/ in my browser then it works fine.
Its like PHPUnit cannot see that host?
$crawler->request() should not receive an actual URI, but the part after the front controller. So in your case, use:
$client = static::createClient();
$crawler = $client->request(
'GET',
'/admin/',
array(),
array(),
array("HTTP_HOST" => "dev.example:8080"));
The reason behind this is that the client doesn't actually request for a page (to a host). It just simulates a request, by creating a Request object, passing it to the AppKernel and then parsing the Response. This is much faster.
If you want to test using a real request, I recommend installing and using Mink or Goutte.
I want to do basic access authentication using Guzzle and I am very new to programming. I have no clue what to do. I tried to do this using curl but my environment requires using guzzle.
If you're using Guzzle 5.0 or newer, the docs say that basic auth is specified using the auth parameter:
$client = new GuzzleHttp\Client();
$response = $client->get('http://www.server.com/endpoint', [
'auth' => [
'username',
'password'
]
]);
Please note that the syntax is different if you're using Guzzle 3.0 or earlier. The constructor is different, and you also need to explicitly use the send method on a request to get a response:
$client = new Guzzle\Http\Client();
$request = $client->get('http://www.server.com/endpoint');
$request->setAuth('username', 'password');
$response = $request->send();
A brief addendum
In response to #Matthwew-Knill, yes, you can set a default authorization and implicitly have Guzzle send it in each further request. #Nick's answer is on point. The client constructor takes every parameter you could think of and then some.
Another approach, if you want to get creative, would be to instantiate the client passing it default headers to send on every further request. Simple auth is, after all, an Authorization header. It's computed as:
$client = new Client([
'headers'=>[
'Authorization'=> Basic base64_encode(<username>:<password>)
]
]);
Last but not least please note that filling a simple auth dialog happens only once (upon the virst visit of a given session). This is usually achieved by setting a cookie on the visitor's browser. That cookie in turn contains enough info for the server to identify its matching active session.
Usually, Guzzle requests are stateless, but you can configure Guzzle with a middleware chain to either modify request or responses, for debug purposes and, for this use case, to remember cookies, thus becoming partially stateful.
Please check the detailed procedure in Guzzle Docs. The important thing is that, by instantiating the client with a cookiejar middleware, therefore having the client include it from then on, the first request will remember the server's set-cookie header, and will send it as every further cookie header, making the server recognize the client as a logged in user. Of course, you could also inspect the first response's headers yourself and send its value from then on.
There might be other ways, but I can't think of another right now.
In additional to #amenadiel answer. Sometimes handy specify auth parameters in constructor:
$client = new Client([
'auth' => ['username', 'password'],
]);
Then every request will use this default auth parameters.
This dint work when I used Guzzlev6 and used the advice from #amenadiel. When you use curl, your syntax would look something like
curl -u someone#gmail.com:password http://service.com
behind the scenes it actually takes the "someone#gmail.com:password" bit, base64 encodes it and sends the request with an "Authorization" Header with the encoded value. For this example, that will be:
Authorization: Basic c29tZW9uZUBnbWFpbC5jb206cGFzc3dvcmQ=
Advice from #amenadiel appended an "auth: username,password" header and hence, my authentication kept failing. To achieve this successfully, just craft the header when you are instantiating a Guzzle Client request, i.e
$client = new GuzzleHttp\Client();
$credentials = base64_encode('someone#gmail.com:password');
$response = $client->get('http://www.server.com/endpoint', [
'Authorization' => ['Basic '.$credentials]
]);
That would append the header as curl would, and whatever service you are trying to connect to will stop yelling at you,
Cheers.
According to the Guzzle 6 documentation, you can do a request with basic authorization as simple as this:
$client = new Client();
$response = $client->request(
'POST', /*instead of POST, you can use GET, PUT, DELETE, etc*/
$url,
[
'auth' => ['username', 'password'] /*if you don't need to use a password, just leave it null*/
]
);
echo $response->getBody();
NOTE: You don't need to use base64_encode() at all because it already does it before the request.
I've tested and it works :)
See more at: Guzzle 6 Documentation
$response = $client->request( 'GET', 'your_url', [
'auth' => [
'your_username',
'your_password'
],
'headers' => [
'if you want to pass something in the headers'
]
]
);
You can also configure the auth params when instantiating the client instead of adding it to each request:
$this->client = new \GuzzleHttp\Client([
'base_uri' => $this->endpoint,
'headers' => [
'Authorization' => ['Basic '.base64_encode($this->username.':'.$this->password)],
],
]);
Here are the various doc links for Guzzle 6:
Creating a Client
Request Options
Auth Request Options
According to what #bourgeois247 said about base64 encoding, the following worked perfectly for me on Guzzle 6:
$client = new Client();
$credentials = base64_encode('username:password');
$response = $client->post('url',
[
'headers' => [
'Authorization' => 'Basic ' . $credentials,
],
]);
If you use it with symfony, you can also define it in your configuration file (config/packages/eight_points_guzzle.yaml for symfony4 or flex or config.yml for the other version)
In your configuration file :
eight_points_guzzle:
clients:
your_service:
# Write here the host where to do requests
base_url: "yourURL"
options:
timeout: 30
auth:
- yourLogin # login
- yourPassword # password
plugin: ~
Then, in your service, controller, etc....
$client = $this->getContainer()->get('eight_points_guzzle.client.your_service');
$response = $client->get('yourRoute');
See : https://packagist.org/packages/eightpoints/guzzle-bundle
How to use goutte but don't send cookies back to the server?
I want to do that because the server can manage sessionid in the URL.
I end up using guzzle and browserkit directly without using goutte. Guzzle let you chose to manage or not cookies http://guzzle.readthedocs.org/en/latest/quickstart.html#cookies.
This solved this problem amount others.
If really you like goutte, I guess you can also delete cookie between each requests.
The only way I see is to instantiate the GuzzleClient yourself and pass it to the Goutte client.
Like this:
use Goutte\Client as GoutteClient;
use GuzzleHttp\Client as GuzzleClient;
$guzzleClient = new GuzzleClient(array('defaults' => array(
'allow_redirects' => false,
'cookies' => false
));
$client = new GoutteClient();
$client->setClient($guzzleClient);
$client->request('GET', 'http://example.org');
Hi i am using phpunit for testing and Symfony\Bundle\FrameworkBundle\Test\WebTestCase for unit testing. So far there were no problem but now we start to use https and my tests are not working anymore.I start to get 301 response code for my every request in my tests. My question is how do i tell to Symfony\Component\HttpKernel\Client to make requests to
https://localhost.com/uri instead of http://localhost.com/uri ?
EDIT
In symfony website http://symfony.com/doc/current/book/testing.html they show how to configure server parameters and there is a code peace like
$client->request(
'GET',
'/demo/hello/Fabien',
array(),
array(),
array(
'CONTENT_TYPE' => 'application/json',
'HTTP_REFERER' => '/foo/bar',
'HTTP_X-Requested-With' => 'XMLHttpRequest',
)
);
I tried to give HTTPS element as mentioned in http://php.net/manual/en/reserved.variables.server.php changed my code as
$client->request('GET',
'/'.$version.'/agencies/'.$agencyId,
array(),
array(),
array('HTTPS' => 'on')
);
However, it is still not working?
Thanks to #WouterJ i changed my client creation from :
static::createClient();
to:
static::createClient(array(),array('HTTPS' => true));
it solved my problem.
It turns out that I cant give HTTP_HOST and HTTPS parameters in client ->request. It should be determined while client creation.
I am using Symfony4 and this how I created my functional test. I have tested following code and it works fine.
namespace App\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class CategoryControllerTest extends WebTestCase
{
public function testList()
{
$client = static::createClient();
$client->request('GET', '/category/list');
$this->assertEquals(200, $client->getResponse()->getStatusCode());
}
}